<?xml version="1.0"?>
<rss version="2.0"><channel><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x644;&#x63A;&#x629; Rust</title><link>https://academy.hsoub.com/programming/rust/page/2/?d=2</link><description>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x644;&#x63A;&#x629; Rust</description><language>ar</language><item><title>&#x627;&#x644;&#x645;&#x624;&#x634;&#x631;&#x627;&#x62A; &#x627;&#x644;&#x630;&#x643;&#x64A;&#x629; Smart Pointers &#x641;&#x64A; &#x631;&#x633;&#x62A; Rust</title><link>https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B0%D9%83%D9%8A%D8%A9-smart-pointers-%D9%81%D9%8A-%D8%B1%D8%B3%D8%AA-rust-r2021/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_07/--Smart-Pointers---Rust.png.3a022385f5879b06b6d61c9628b3faca.png" /></p>
<p>
	يُعد المؤشر pointer مفهومًا عامًا لمتغيرٍ يحتوي على عنوان في الذاكرة، ويشير هذا العنوان أو "يؤشر إلى" بعض البيانات الأخرى. أكثر أنواع المؤشرات شيوعًا في رست هو المرجع reference، الذي تعلمناه <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D9%84%D9%83%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1786/" rel="">سابقًا</a>. يُحدّد المرجع بالرمز "&amp;" وتُستعار القيمة التي يشير إليها، ولا يوجد للمؤشرات أي قدرات خاصة عدا الإشارة إلى البيانات، ولا يتطلّب استخدامها أي حِمل إضافي overhead.
</p>

<p>
	من جهة أخرى، تُعدّ المؤشرات الذكية smart pointers هياكل بيانات تعمل مثل مؤشر ولكن لها أيضًا بيانات وصفية metadata وقدرات إضافية، إذ لا يقتصر مفهوم المؤشرات الذكية على رست، فهي نشأت في <a href="https://academy.hsoub.com/programming/cpp/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B0%D9%83%D9%8A%D8%A9-smart-pointers-%D9%81%D9%8A-cpp-r913/" rel="">لغة سي بلس بلس C++‎</a> وتوجد بلغات أخرى أيضًا. تحتوي رست على مجموعة متنوعة من المؤشرات الذكية المعرَّفة في المكتبة القياسية التي تقدم وظائف أكثر من تلك التي توفرها المراجع، وللتعرف على المفهوم العام، سنلقي نظرةً على بعض الأمثلة المختلفة للمؤشرات الذكية، بما في ذلك نوع مؤشر ذكي لعدّ المراجع reference counting. يمكّنك هذا المؤشر من السماح بوجود عدّة مالكين owners للبيانات من خلال تتبع عددهم، ويُحرّر البيانات في حال لم يتبقَّ أي مالكين.
</p>

<p>
	يوجد بمفهوم رست للملكية والاستعارة فرقٌ إضافي بين المراجع والمؤشرات الذكية؛ إذ بينما تستعير المراجع البيانات فقط، تمتلك المؤشرات الذكية في كثير من الحالات البيانات التي تشير إليها.
</p>

<p>
	صادفنا مسبقًا بعض المؤشرات الذكية -على الرغم من أننا لم ندعوها على هذا النحو في ذلك الوقت- بما في ذلك <code>String</code> و <code>&lt;Vec&lt;T</code>، وكلا النوعين مؤشرات ذكية لأنهما يمتلكان بعض الذاكرة و تسمحان لك بالتلاعب بها، إضافةً لوجود بيانات وصفية وإمكانيات أو ضمانات إضافية. تخزّن <code>String</code> على سبيل المثال سعتها على أنها بيانات وصفية ولديها قدرة إضافية لتضمن أن تكون بياناتها دائمًا بترميز <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D8%A8%D8%AA%D8%B1%D9%85%D9%8A%D8%B2-utf-8-%D8%AF%D8%A7%D8%AE%D9%84-%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1876/" rel="">UTF-8</a> صالح.
</p>

<p>
	تُطبَّق عادةً المؤشرات الذكية باستخدام الهياكل، وتنفّذ المؤشرات الذكية على عكس البنية العادية <code>Deref</code> و <code>Drop</code>، إذ تسمح سمة <code>Deref</code> لنسخة instance من هيكل المؤشر الذكي بالتصرف بمثابة مرجع حتى تتمكن من كتابة شيفرتك البرمجية للعمل مع المراجع أو المؤشرات الذكية، بينما تسمح لك سمة <code>Drop</code> بتخصيص الشيفرة التي تُنفَّذ عندما تخرج نسخة المؤشر الذكي عن النطاق، وسنناقش هنا كلًا من <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%B3%D9%85%D8%A7%D8%AA-traits-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1951/" rel="">السمات traits</a> ونوضح سبب أهميتها للمؤشرات الذكية.
</p>

<p>
	لن يغطي هذا المقال كل مؤشر ذكي موجود بما أن نمط المؤشر الذكي smart pointer pattern هو نمط تصميم عام يستخدم بصورةٍ متكررة في رست. تمتلك العديد من المكتبات مؤشراتها الذكية الخاصة بها، ويمكنك حتى كتابة المؤشرات الخاصة بك. سنغطي المؤشرات الذكية الأكثر شيوعًا في المكتبة القياسية:
</p>

<ul>
	<li>
		<code>&lt;Box&lt;T</code> لحجز مساحة خاصة بالقيم على الكومة heap.
	</li>
	<li>
		<code>&lt;Rc&lt;T</code> نوع عدّ مرجع يمكّن الملكية المتعددة.
	</li>
	<li>
		<code>&lt;Ref&lt;T</code> و <code>&lt;RefMut&lt;T</code> اللذين يمكن الوصول إليهما عن طريق <code>&lt;RefCell&lt;T</code>، وهو نمط يفرض قواعد الاستعارة وقت التنفيذ runtime بدلًا من وقت التصريف compile time.
	</li>
</ul>

<p>
	سنغطي بالإضافة إلى ذلك نمط قابلية التغيير الداخلي interior mutability pattern، إذ يعرّض النوع الثابت immutable واجهة برمجية لتعديل قيمة داخلية، كما سنناقش أيضًا دورات المرجع reference cycles، وسنرى كيف بإمكانها تسريب leak الذاكرة وكيفية منعها من ذلك.
</p>

<p>
	دعنا نبدأ.
</p>

<h2>
	استخدام المؤشر Box<t>‎ للإشارة إلى البيانات المخزنة على الكومة</t>
</h2>

<p>
	يُعد الصندوق "Box" واحدًا من أكثر المؤشرات الذكية وضوحًا وبساطةً، ويُكتب نوعه بالشكل <code>&lt;Box&lt;T</code>. تسمح لك الصناديق أن تخزن البيانات على الكومة بدلًا من المكدس stack، إذ يبقى المؤشر على المكدس الذي يشير بدوره للبيانات الموجودة على الكومة. عد إلى الفصل <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%B1%D8%A7%D8%AC%D8%B9-references-%D9%88%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D8%A7%D8%B1%D8%A9-borrowing-%D9%88%D8%A7%D9%84%D8%B4%D8%B1%D8%A7%D8%A6%D8%AD-slices-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1787/" rel="">المراجع References والاستعارة Borrowing والشرائح Slices في لغة رست</a> لمراجعة الفرق بين الكومة والمكدس.
</p>

<p>
	لا تملك الصناديق أي أفضلية في الأداء عدا أنها تخزن بياناتها على الكومة عوضًا عن المكدس، ولا تملك الكثير من الإمكانيات الإضافية. سنستخدمها غالبًا في أحد هذه الحالات:
</p>

<ul>
	<li>
		عندما يكون لديك نوع بحجم غير معروف وقت التصريف وتريد أن تستخدم قيمةً لهذا النوع في سياق يتطلب حجمه المحدد.
	</li>
	<li>
		عندما يكون لديك حجم كبير من البيانات وتريد أن تنقل ملكيتها ولكنك تريد التيقن أن البيانات لن تُنسَخ عندما تفعل هذا.
	</li>
	<li>
		عندما تريد أن تملك قيمةً ما وتهتم فقط أنها من نوع يناسب سمة محددة بدلًا عن كونها من نوع محدد.
	</li>
</ul>

<p>
	سنستعرض الحالة الأولى في فقرة "تمكين الأنواع التعاودية باستخدام الصناديق"، أما في الحالة الثانية فيمكن أن يأخذ نقل ملكية كبيرة من البيانات وقتًا طويلًا وذلك لأن البيانات نُسخت على المكدس، ويمكننا تخزين الكمية الكبيرة من البيانات على الكومة في صندوق لتحسين الأداء في هذه الحالة، وبذلك تُنسخ كميةٌ صغيرةٌ من بيانات المؤشر على المكدس، بينما تبقى البيانات التي تشير إليها في مكان واحد على الكومة. تُعرف الحالة الثالثة باسم "سمة الكائن" وسنتكلم عنها لاحقًا، إذ أنك ستطبق ما تعلمته هنا لاحقًا.
</p>

<h3>
	استخدام Box<t>‎ لتخزين البيانات على الكومة</t>
</h3>

<p>
	سنتكلم عن طريقة كتابة <code>Box&lt;T&gt;‎</code> وكيفية تفاعل هذا النوع مع القيم المخزنة داخله قبل أن نناقش حالة استخدام تخزين الكومة للنوع <code>Box&lt;T&gt;‎</code>.
</p>

<p>
	توضح الشيفرة 1 كيفية استخدام صندوق لتخزين قيمة <code>i32</code> على الكومة:
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9975_12" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let b </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="lit">5</span><span class="pun">);</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"b = {}"</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 1: تخزين قيمة من النوع <code>i32</code> على الكومة باستعمال صندوق box]
</p>

<p>
	نعرّف المتغير <code>b</code> ليملك القيمة <code>Box</code> التي تشير إلى القيمة "5" المخزنة على الكومة. سيطبع هذا البرنامج "b = 5" وفي هذه الحالة سنصل للبيانات الموجودة في الصندوق بطريقة مشابهة في حال كانت البيانات مخزنة على المكدس. ستُحرَّر القيمة deallocated كما في أي قيمة ممتلكة، عندما يخرج صندوق عن النطاق كما تفعل <code>b</code> في نهاية <code>main</code>، وتحدث عملية التحرير لكل من الصندوق (المخزن على المكدس) والبيانات التي يشير إليها (المخزنة على الكومة).
</p>

<p>
	وضع قيمة وحيدة على الكومة غير مفيد، لأنك لن تستخدم الصناديق بحد ذاتها كثيرًا، ووجود قيم مثل قيمة وحيدة من النوع <code>i32</code> على المكدس -إذ تُخزن افتراضيًا هناك- مناسبٌ أكثر في أغلب الحالات. لننظر إلى حالة تسمح لنا الصناديق أن نعرّف أنواع لن يُسمح لنا بتعريفها إن لم يكن لدينا صناديق.
</p>

<h3>
	تمكين الأنواع التعاودية باستخدام الصناديق
</h3>

<p>
	يمكن للقيمة من نوع تعاودي recursive type أن تملك قيمةً أخرى من النوع ذاته مثل جزء من نفسها. تمثّل الأنواع التعاودية مشكلة إذ أن رست تحتاج لمعرفة المساحة التي يحتلها نوع ما وقت التصريف، ويمكن لتداخل قيم الأنواع التعاودية نظريًا أن يستمر إلى ما لا نهاية، لهذا لا يمكن أن تعرف رست كم تحتاج القيمة من مساحة، إلا أنه يمكننا استخدام الأنواع التعاودية بإدخال صندوق في تعريف النوع التعاودي نظرًا لأن الصناديق لها حجم معروف.
</p>

<p>
	لنكتشف قائمة البنية "cons list" مثالًا على نوع تعاودي، إذ أن نوع البيانات هذا موجود كثيرًا في لغات البرمجة الوظيفية. يُعَدّ نوع قائمة البنية بسيطًا وواضحًا باستثناء نقطة التعاود فيه، وبالتالي ستكون المفاهيم في الأمثلة التي سنعمل عليها مفيدةً في أي وقت ستصادف فيه حالات أكثر تعقيدًا من ضمنها الأنواع التعاودية.
</p>

<h4>
	المزيد من المعلومات عن قائمة البنية
</h4>

<p>
	تُعد قائمة البنية <a href="https://academy.hsoub.com/programming/general/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-data-structures/" rel="">هيكل بيانات</a> أتى من لغة البرمجة ليسب Lisp وشبيهاتها، وتتألف من أزواج متداخلة، وهي نسخة ليسب من القائمة المترابطة linked list، ويأتي اسم هيكل البيانات هذا من الدالة <code>cons</code> (اختصارًا لدالة البنية construct function) في ليسب التي تبني بدورها زوجًا جديدًا من وسيطين arguments. يمكننا بناء قوائم بنية مؤلفة من أزواج تعاودية عن طريق استدعاء <code>cons</code> على زوج يحتوي على قيمة وزوج آخر.
</p>

<p>
	إليك المثال التوضيحي pseudocode لقائمة بنية تحتوي على القائمة 1، 2، 3 مع وجود كل زوج داخل قوسين:
</p>

<pre class="ipsCode">(1, (2, (3, Nil)))
</pre>

<p>
	يحتوي كل عنصر في قائمة البنية على عنصرين: القيمة للعنصر الحالي والعنصر التالي، إلا أن العنصر الأخير في القائمة يحتوي فقط على قيمة تُدعى <code>Nil</code> دون عنصر تالي. يمكن إنشاء قائمة البنية عن طريق استدعاء دالة <code>cons</code> بصورة تعاودية، والاسم المتعارف عليه للدلالة على الحالة الأساسية base case للتعاودية هو <code>Nil</code>، مع العلم أنه ليس خاضعًا لنفس مبدأ المصطلحين "null" أو "nil" الذين ناقشناهما سابقًا، فهما يمثلان مؤشرًا على قيمة غير موجودة أو غير صالحة.
</p>

<p>
	لا تعد قائمة بينة من هياكل البيانات المُستخدمة بكثرة في رست، إذ يُعد النوع <code>&lt;Vec&lt;T</code> خيارًا أفضل للاستعمال في معظم الوقت عندما تملك قائمة عناصر في رست، كما يوجد أنواع أخرى لبيانات تعاودية مفيدة في حالات متعددة، لكن من خلال البدء بقائمة البنية هنا، سنتعرف كيف تمكّننا للصناديق من تعريف نوع بيانات تعاودية دون ارتباك.
</p>

<p>
	تتضمن الشيفرة 2 على تعريف لمعدّد enum لقائمة بنية. لاحظ أن هذه الشيفرة لن تُصرَّف بعد لأن النوع <code>List</code> لا يملك حجمًا محددًا وهذا ما سنوضحه لاحقًا.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9975_15" style=""><span class="kwd">enum</span><span class="pln"> </span><span class="typ">List</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Cons</span><span class="pun">(</span><span class="pln">i32</span><span class="pun">,</span><span class="pln"> </span><span class="typ">List</span><span class="pun">),</span><span class="pln">
    </span><span class="typ">Nil</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 2: المحاولة الأولى لتعريف معدّد لتمثيل هيكل البيانات قائمة البنية لقيم من النوع <code>i32</code>]
</p>

<p>
	<strong>ملاحظة</strong>: نطبّق قائمة البنية التي تحمل فقط قيم من النوع <code>i32</code> بهدف التوضيح، إذ يمكننا تنفيذها باستعمال الأنواع المعمّمة generics كما ناقشنا <a href="https://academy.hsoub.com/programming/rust/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D9%85%D8%A9-generic-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-rust-r1935/" rel="">سابقًا</a> وذلك لتعريف نوع قائمة بنية يخزّن قيمًا من أي نوع.
</p>

<p>
	يبدو استعمال النوع <code>List</code> لتخزين القائمة "1‎, 2, 3" كما توضح الشيفرة 3:
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9975_17" style=""><span class="pln">use crate</span><span class="pun">::</span><span class="typ">List</span><span class="pun">::{</span><span class="typ">Cons</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Nil</span><span class="pun">};</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let </span><span class="typ">list</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Cons</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Cons</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Cons</span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Nil</span><span class="pun">)));</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 3: استعمال المعدّد <code>List</code> لتخزين القائمة "1‎, 2, 3"]
</p>

<p>
	تحمل قيمة <code>Cons</code> الأولى على "1" وقيمة <code>List</code> أخرى، قيمة <code>List</code> هذه هي قيمة <code>Cons</code> أخرى تحتوي على "2" وقيمة <code>List</code> أخرى، قيمة <code>List</code> هذه هي قيمة <code>Cons</code> أخرى تحتوي على "3" وقيمة <code>List</code> التي هي في النهاية <code>Nil</code> ألا وهو المتغاير variant غير التعاودي الذي يشير إلى نهاية القائمة.
</p>

<p>
	إذا حاولنا تصريف الشيفرة البرمجية الموجودة في الشيفرة 3، فسنحصل على الخطأ الموضح في الشيفرة 4:
</p>

<pre class="ipsCode">$ cargo run
   Compiling cons-list v0.1.0 (file:///projects/cons-list)
error[E0072]: recursive type `List` has infinite size
 --&gt; src/main.rs:1:1
  |
1 | enum List {
  | ^^^^^^^^^ recursive type has infinite size
2 |     Cons(i32, List),
  |               ---- recursive without indirection
  |
help: insert some indirection (e.g., a `Box`, `Rc`, or `&amp;`) to make `List` representable
  |
2 |     Cons(i32, Box&lt;List&gt;),
  |               ++++    +

For more information about this error, try `rustc --explain E0072`.
error: could not compile `cons-list` due to previous error
</pre>

<p style="text-align: center;">
	[الشيفرة 4: الخطأ الذي نحصل عليه عندما نحاول تعريف معدّد تعاودي]
</p>

<p>
	يُظهِر الخطأ أن هذا النمط "له حجم لا نهائي"، والسبب هو تعريفنا للنوع <code>List</code> بمتغير تعاودي، أي أنه يحمل قيمةً أخرى لنفسه مباشرًة، ونتيجة لذلك، لا تستطيع رست معرفة مقدار المساحة التي يحتاجها لتخزين قيمة <code>List</code>. دعونا نوضح لماذا نحصل على هذا الخطأ. أولًا، سننظر إلى كيفية تحديد رست لمقدار المساحة التي يحتاجها لتخزين قيمة لنوع غير تعاودي.
</p>

<h3>
	حساب حجم نوع غير تعاودي
</h3>

<p>
	تذكر معدّد <code>Message</code> الذي عرّفناه سابقًا (الشيفرة 2 من الفصل <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%B9%D8%AF%D8%A7%D8%AF%D8%A7%D8%AA-enums-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1851/" rel="">التعدادات enums في لغة رست Rust</a>)عندما ناقشنا تعريفات المعدّد:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9975_19" style=""><span class="kwd">enum</span><span class="pln"> </span><span class="typ">Message</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Quit</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">Move</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> x</span><span class="pun">:</span><span class="pln"> i32</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> i32 </span><span class="pun">},</span><span class="pln">
    </span><span class="typ">Write</span><span class="pun">(</span><span class="typ">String</span><span class="pun">),</span><span class="pln">
    </span><span class="typ">ChangeColor</span><span class="pun">(</span><span class="pln">i32</span><span class="pun">,</span><span class="pln"> i32</span><span class="pun">,</span><span class="pln"> i32</span><span class="pun">),</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{}</span></pre>

<p>
	تمر رست عبر كل من المتغيرات لمعرفة المتغير الذي يحتاج إلى أكبر مساحة وذلك لتحديد مقدار المساحة المراد تخصيصها لقيمة <code>Message</code>. ترى رست أن <code>Message::Quit</code> لا تحتاج إلى أي مساحة، بينما تحتاج <code>Message::Move</code> إلى مساحة كافية لتخزين قيمتين من نوع <code>i32</code>، وهكذا دواليك، ونظرًا لاستخدام متغير واحد فقط فإن أكبر مساحة تحتاجها قيمة <code>Message</code> هي المساحة التي ستأخذها لتخزين أكبر متغيراتها.
</p>

<p>
	قارن هذا مع ما يحدث عندما تحاول رست تحديد مقدار المساحة التي يحتاجها نوع تعاودي مثل المعدد <code>List</code> في الشيفرة 2، إذ يبدأ المصرّف بالنظر إلى المتغاير <code>Cons</code> الذي يحمل قيمةً من النوع <code>i32</code> وقيمةً من النوع <code>List</code>، لذلك يحتاج <code>Cons</code> إلى مساحة مساوية لحجم النوع <code>i32</code> إضافةً إلى حجم النوع <code>List</code>. لمعرفة مقدار الذاكرة التي يحتاجها النوع <code>List</code> ينظر المصرّف إلى المتغايرات بدءًا من المتغاير <code>Cons</code>، الذي يحمل قيمةً من النوع <code>i32</code> وقيمةً من النوع <code>List</code>، وتستمر هذه العملية لما لا نهاية، كما هو موضح في الشكل 1.
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="130210" href="https://academy.hsoub.com/uploads/monthly_2023_07/15-01.png.cdb7905539b17005b3b18dd09b49d6cb.png" rel=""><img alt="15-01.png" class="ipsImage ipsImage_thumbnailed" data-fileid="130210" data-unique="r7zr6qmn7" src="https://academy.hsoub.com/uploads/monthly_2023_07/15-01.thumb.png.05a82f794ec176bdaac49e4751217d7d.png"> </a>
</p>

<p style="text-align: center;">
	[الشكل 1: <code>List</code> لانهائية مؤلفة من متغايرات <code>Cons</code> لانهائية]
</p>

<h3>
	استخدام &lt;Box&lt;T للحصول على نوع تعاودي بحجم معروف
</h3>

<p>
	يعطينا المصرّف الخطأ التالي لأن رست لا يمكنها معرفة مقدار المساحة المراد تخصيصها لأنواع معرّفة بصورةٍ تعاودية مرفقًا مع هذا الاقتراح المفيد:
</p>

<pre class="ipsCode">help: insert some indirection (e.g., a `Box`, `Rc`, or `&amp;`) to make `List` representable
  |
2 |     Cons(i32, Box&lt;List&gt;),
  |               ++++    +
</pre>

<p>
	يعني "التحصيل indirection" -في هذا الاقتراح- أنه بدلًا من تخزين قيمة مباشرةً، يجب علينا تغيير هيكل البيانات المُستخدَم لتخزين القيمة بصورةٍ غير مباشرة عن طريق تخزين مؤشر يشير إلى القيمة عوضًا عن ذلك.
</p>

<p>
	نظرًا لأن <code>&lt;Box&lt;T</code> هو مؤشر فإن رست تعرف دائمًا مقدار المساحة التي يحتاجها <code>&lt;Box&lt;T</code>، إذ أن حجم المؤشر لا يتغير بناءً على كمية البيانات التي يشير إليها، وهذا يعني أنه يمكننا وضع <code>&lt;Box&lt;T</code> داخل المتغاير <code>Cons</code> بدلًا من قيمة <code>List</code> أخرى مباشرةً. سيشير <code>&lt;Box&lt;T</code> إلى قيمة <code>List</code> التالية التي ستكون على الكومة بدلًا من داخل المتغاير <code>Cons</code>. نظريًا، لا يزال لدينا قائمة أنشئت باستخدام قوائم تحتوي على قوائم أخرى، ولكن هذا التطبيق الآن أشبه بوضع العناصر بجانب بعضها بدلًا من وضع بعضها داخل الأخرى.
</p>

<p>
	يمكننا تغيير تعريف معدّد <code>List</code> في الشيفرة 2 باستخدام <code>List</code> في الشيفرة 3 كما هو موضح في الشيفرة 5 التي ستصرَّف بنجاح:
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9975_22" style=""><span class="kwd">enum</span><span class="pln"> </span><span class="typ">List</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Cons</span><span class="pun">(</span><span class="pln">i32</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="typ">List</span><span class="pun">&gt;),</span><span class="pln">
    </span><span class="typ">Nil</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

use crate</span><span class="pun">::</span><span class="typ">List</span><span class="pun">::{</span><span class="typ">Cons</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Nil</span><span class="pun">};</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let </span><span class="typ">list</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Cons</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Cons</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Cons</span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Nil</span><span class="pun">))))));</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 5: تعريف <code>List</code> التي تستخدم <code>&lt;Box&lt;T</code> للحصول على حجم معروف]
</p>

<p>
	يحتاج المتغاير <code>Cons</code> إلى حجم<code>i32</code> بالإضافة إلى مساحة لتخزين بيانات مؤشر الصندوق، وبما أن المتغاير <code>Nil</code> لا يخزن أي قيم فهو يحتاج إلى مساحة أقل من المتغاير <code>Cons</code>. نعلم الآن أن أي قيمة <code>List</code> ستشغل حجم <code>i32</code> إضافةً إلى حجم بيانات مؤشر الصندوق. كسرنا السلسلة اللانهائية التعاودية باستخدام الصندوق، بحيث يمكن للمصرف الآن معرفة الحجم الذي يحتاجه لتخزين قيمة <code>List</code>. يوضح الشكل 2 ما يبدو عليه متغاير <code>Cons</code> الآن.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="130209" href="https://academy.hsoub.com/uploads/monthly_2023_07/15-02.png.5c74dfd3b5908cc51ee529a2dd56ac5e.png" rel=""><img alt="15-02.png" class="ipsImage ipsImage_thumbnailed" data-fileid="130209" data-unique="r8aaatjt1" src="https://academy.hsoub.com/uploads/monthly_2023_07/15-02.png.5c74dfd3b5908cc51ee529a2dd56ac5e.png"> </a>
</p>

<p style="text-align: center;">
	[الشكل 2: <code>List</code> ذات حجم محدد لأن <code>Cons</code> يحمل <code>Box</code>]
</p>

<p>
	توفر الصناديق التحصيل وتخصيص الكومة فقط، إذ لا تتوافر على أي إمكانيات خاصة أخرى مثل تلك التي سنراها مع أنواع المؤشرات الذكية الأخرى لاحقًا، كما أنها لا تتمتع بالأداء العام الذي تتحمله هذه الإمكانيات الخاصة لتكون مفيدةً في حالات مثل قائمة البنية، إذ تكون ميزة التحصيل هي الميزة الوحيدة التي نحتاجها. سنلقي نظرةً على المزيد من حالات استعمال الصناديق لاحقًا.
</p>

<p>
	النوع <code>&lt;Box&lt;T</code> هو مؤشر ذكي لأنه يطبق السمة <code>Deref</code> التي تسمح لقيم <code>&lt;Box&lt;T</code> أن تُعامَل بمثابة مراجع. عندما تخرج قيمة <code>&lt;Box&lt;T</code> عن النطاق، تُمسَح بيانات الكومة التي يشير إليها الصندوق بسبب تطبيق السمة <code>Drop</code>. ستبرز أهمية هاتان السمتان أكثر عندما نناقش أنواع المؤشرات الذكية الأخرى لاحقًا. لنكتشف هاتين السمتين بتفاصيل أكثر.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch15-00-smart-pointers.html" rel="external nofollow">Smart Pointers</a> من كتاب <a href="https://doc.rust-lang.org/stable/book/title-page.html/" rel="external nofollow">The Rust Programming Language</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/rust/%D9%85%D8%B9%D8%A7%D9%85%D9%84%D8%A9-%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B0%D9%83%D9%8A%D8%A9-smart-pointers-%D9%85%D8%AB%D9%84-%D9%85%D8%B1%D8%A7%D8%AC%D8%B9-%D9%86%D9%85%D8%B7%D9%8A%D8%A9-regular-references-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D9%85%D8%A9-deref-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2029/" rel="">معاملة المؤشرات الذكية Smart Pointers كمراجع نمطية باستخدام سمة Deref في لغة رست</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D9%85%D8%B3%D8%A7%D8%AD%D8%A9-%D8%B9%D9%85%D9%84-%D9%83%D8%A7%D8%B1%D8%AC%D9%88-cargo-workspaces-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D9%88%D8%AA%D8%AD%D9%85%D9%8A%D9%84-%D8%AD%D8%B2%D9%85%D8%A9-%D9%85%D9%86-cratesio-r2014/" rel="">مساحة عمل كارجو Cargo Workspaces في لغة رست وتحميل حزمة من crates.io</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-data-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1780/" rel="">أنواع البيانات Data Types في لغة رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/cpp/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B0%D9%83%D9%8A%D8%A9-smart-pointers-%D9%81%D9%8A-cpp-r913/" rel="">المؤشرات الذكية (Smart Pointers) في Cpp</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2021</guid><pubDate>Wed, 05 Jul 2023 13:00:00 +0000</pubDate></item><item><title>&#x645;&#x633;&#x627;&#x62D;&#x629; &#x639;&#x645;&#x644; &#x643;&#x627;&#x631;&#x62C;&#x648; Cargo Workspaces &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A; &#x648;&#x62A;&#x62D;&#x645;&#x64A;&#x644; &#x62D;&#x632;&#x645;&#x629; &#x645;&#x646; crates.io</title><link>https://academy.hsoub.com/programming/rust/%D9%85%D8%B3%D8%A7%D8%AD%D8%A9-%D8%B9%D9%85%D9%84-%D9%83%D8%A7%D8%B1%D8%AC%D9%88-cargo-workspaces-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D9%88%D8%AA%D8%AD%D9%85%D9%8A%D9%84-%D8%AD%D8%B2%D9%85%D8%A9-%D9%85%D9%86-cratesio-r2014/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_07/---Cargo-Workspaces----Rust.png.036ab5849e687dd4859d66c311cbb59e.png" /></p>
<p>
	بنينا سابقًا حزمة تتضمن وحدة تنفيذية مصرفة Binary Crate ووحدة مكتبة مصرفة Library Crate، وقد تجد مع تطور مشروعك أن وحدة المكتبة المصرفة تزداد حجمًا وستحتاج إلى تقسيم حزمتك إلى عدد من وحدات مكتبة مصرفة. يقدّم كارجو Cargo ميزة تدعى مساحات العمل Workspaces التي تساعد على إدارة حزم متعددة مرتبطة تُطوَّر بالترادف tandem أي واحدًا بعد الآخر.
</p>

<h2>
	إنشاء مساحة عمل
</h2>

<p>
	مساحة العمل هي مجموعة من الحزم التي تتشارك ملف Cargo.lock ومجلد الخرج ذاتهما. سنستخدم شيفرة برمجية بسيطة لإنشاء مشروع باستخدام مساحة العمل، بهدف التركيز على بُنية مساحة العمل أكثر. هناك الكثير من الطرق لبناء مساحة العمل ولذا سنعمل وفق الطريقة الشائعة. سيكون لدينا مساحة عمل تحتوي على وحدة ثنائية أو تنفيذية واحدة ومكتبتين؛ إذ ستؤمن الوحدة الثنائية الوظيفة الأساسية، وستعتمد بدورها على مكتبتين: مكتبة تؤمن دالة <code>add_one</code>، والثانية ستؤمن دالة <code>add_two</code>. ستكون الوحدات المصرفة الثلاثة في مساحة العمل ذاتها. نبدأ بعمل مسار جديد لمساحة العمل:
</p>

<pre class="ipsCode">$ mkdir add
$ cd add
</pre>

<p>
	ننشئ بعد ذلك في مجلد الخرج add الملف Cargo.toml الذي سيضبط مساحة العمل كاملةً. لن يكون لهذا الملف قسم <code>[package]</code>. بل سيبدأ بقسم <code>[workspace]</code> الذي سيسمح لنا بإضافة أعضاء إلى مساحة العمل عن طريق تحديد المسار للحزمة باستخدام الوحدة الثنائية أو التنفيذية المصرفة، وفي هذه الحالة المسار هو "adder":
</p>

<p>
	اسم الملف: Cargo.toml
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5011_8" style=""><span class="pun">[</span><span class="pln">workspace</span><span class="pun">]</span><span class="pln">

members </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="str">"adder"</span><span class="pun">,</span><span class="pln">
</span><span class="pun">]</span></pre>

<p>
	ننشئ وحدة ثنائية مصرفة <code>[adder]</code> عن طريق تنفيذ <code>cargo new</code> في المجلد add:
</p>

<pre class="ipsCode">$ cargo new adder
     Created binary (application) `adder` package
</pre>

<p>
	يمكننا الآن بناء مساحة العمل عن طريق تشغيل <code>cargo build</code>. الملفات في مجلد add يجب أن تكون على النحو التالي:
</p>

<pre class="ipsCode">├── Cargo.lock
├── Cargo.toml
├── adder
│   ├── Cargo.toml
│   └── src
│       └── main.rs
└── target
</pre>

<p>
	لمساحة العمل مجلد "target" واحد في بداية المستوى الذي ستوضع فيه أدوات التخطيط artifacts المصرفة، ولا تحتوي حزمة <code>adder</code> على مجلد "target". حتى لو نفّذنا <code>cargo build</code> داخل مجلد "adder"، ستكون أدوات التخطيط المصرفة في "add/target" بدلاً من "add/adder/target". يهيّئ كارجو المجلد target بالشكل هذا لأن الحزم المصرفة في مساحة العمل مهيئة لتعتمد على بعضها بعضًا. إذا كان لكل حزمة مصرفة مجلد "target" خاص بها، فهذا يعني أن كل حزمة مصرفة ستُعيد تصريف باقي الحزم المصرفة في مساحة العمل لوضع أدوات التخطيط في مجلد "target" الخاص بها، إلا أن الحزم تتجنب عملية إعادة البناء غير الضرورية بمشاركة مجلد "target" واحد.
</p>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة تطوير التطبيقات باستخدام لغة Python
		</p>

		<p class="banner-subtitle">
			احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/python-application-development" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<img alt="دورة تطوير التطبيقات باستخدام لغة Python" src="https://academy.hsoub.com/learn/assets/images/courses/python-application-development.png">
	</div>
</div>

<h2>
	إنشاء الحزمة الثانية في مساحة العمل
</h2>

<p>
	دعنا ننشئ حزمة عضو ثانية في مساحة العمل ونسميها <code>add_one</code>. غيِّر ملف Cargo.toml الموجود في المستوى العلوي ليحدد المسار add_one في القائمة <code>members</code>:
</p>

<p>
	اسم الملف: Cargo.toml
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5011_11" style=""><span class="pun">[</span><span class="pln">workspace</span><span class="pun">]</span><span class="pln">

members </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="str">"adder"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"add_one"</span><span class="pun">,</span><span class="pln">
</span><span class="pun">]</span></pre>

<p>
	أنشئ بعد ذلك حزمة مكتبة مصرفة اسمها <code>add_one</code>:
</p>

<pre class="ipsCode">$ cargo new add_one --lib
     Created library `add_one` package
</pre>

<p>
	يجب أن يحتوي مجلد "add" الآن على المجلدات والملفات التالية:
</p>

<pre class="ipsCode">├── Cargo.lock
├── Cargo.toml
├── add_one
│   ├── Cargo.toml
│   └── src
│       └── lib.rs
├── adder
│   ├── Cargo.toml
│   └── src
│       └── main.rs
└── target
</pre>

<p>
	نضيف في الملف add_one/scr/lib.rs الدالة <code>add_one</code>:
</p>

<p>
	اسم الملف: add_one/src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5011_13" style=""><span class="pln">pub fn add_one</span><span class="pun">(</span><span class="pln">x</span><span class="pun">:</span><span class="pln"> i32</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> i32 </span><span class="pun">{</span><span class="pln">
    x </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الآن بإمكاننا أن نجعل كلًا من الحزمة <code>adder</code> والوحدة الثنائية المصرفة تعتمدان على حزمة <code>add_one</code> التي تحتوي مكتبتنا. أولاً، نضيف اعتمادية مسار <code>add_one</code> إلى الملف adder/Cargo.toml.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5011_16" style=""><span class="pun">[</span><span class="pln">dependencies</span><span class="pun">]</span><span class="pln">
add_one </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> path </span><span class="pun">=</span><span class="pln"> </span><span class="str">"../add_one"</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	لا يفترض كارجو أن الحزم المصرفة تعتمد على بعضها في مساحة العمل، لذا نحتاج إلى توضيح علاقات الاعتمادية.
</p>

<p>
	لنستخدم بعدها دالة <code>add_one</code> (من الحزمة المصرفة <code>add_one</code>) في الحزمة المصرفة <code>adder</code>. افتح الملف adder/scr/main.rs وأضف سطر <code>use</code> في الأعلى لإضافة حزمة المكتبة المصرفة <code>add_one</code> الجديدة إلى النطاق. ثم عدِّل الدالة <code>main</code> بحيث تستدعي الدالة <code>add_one</code> كما في الشيفرة 7.
</p>

<p>
	اسم الملف: adder/src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5011_18" style=""><span class="pln">use add_one</span><span class="pun">;</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let num </span><span class="pun">=</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"Hello, world! {num} plus one is {}!"</span><span class="pun">,</span><span class="pln"> add_one</span><span class="pun">::</span><span class="pln">add_one</span><span class="pun">(</span><span class="pln">num</span><span class="pun">));</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	[الشيفرة 7: استخدام حزمة المكتبة المصرفة <code>add_one</code> من الحزمة <code>adder</code>]
</p>

<p>
	دعنا نبني مساحة العمل بتنفيذ <code>cargo build</code> في مجلد "add" العلوي.
</p>

<pre class="ipsCode">$ cargo build
   Compiling add_one v0.1.0 (file:///projects/add/add_one)
   Compiling adder v0.1.0 (file:///projects/add/adder)
    Finished dev [unoptimized + debuginfo] target(s) in 0.68s
</pre>

<p>
	يمكننا تحديد أي حزمة نريد تشغيلها في مساحة العمل باستخدام الوسيط <code>‎-p</code> واسم الحزمة مع <code>cargo run</code> لتشغيل الحزمة الثنائية المصرفة من المجلد "add":
</p>

<pre class="ipsCode">$ cargo run -p adder
    Finished dev [unoptimized + debuginfo] target(s) in 0.0s
     Running `target/debug/adder`
Hello, world! 10 plus one is 11!
</pre>

<p>
	يشغل هذا الأمر الشيفرة الموجودة في الملف "adder/scr/main.rs"، والتي تعتمد على الحزمة المصرفة <code>add_one</code>.
</p>

<h3>
	الاعتماد على حزمة خارجية في مساحة العمل
</h3>

<p>
	نلاحظ أن مساحة العمل تحتوي على ملف Cargo.lock واحد في المستوى الأعلى، بدلاً من أن يكون هناك ملف Cargo.lock في كل مسار حزمة مصرفة. يضمن ذلك أن كل حزمة مصرفة تستخدم الإصدار ذاته لكل الاعتماديات. إذا أضفنا حزمة <code>rand</code> للملفين "adder/Cargo.toml" و "add_one/Cargo.toml"، سيحوِّل كارجو كلاهما إلى إصدار واحد من <code>rand</code>، ثم سيسجل ذلك في Cargo.lock. جعل كل حزم المصرفة تستخدم نفس الاعتمادية يعني أن كل الحزم المصرفة ستكون متوافقة مع بعضها. دعنا نضيف الحزمة المصرفة <code>rand</code> إلى قسم <code>[dependencies]</code> في ملف add_one/Cargo.toml لكي نستخدم الحزمة المصرفة <code>rand</code> في الحزمة المصرفة <code>add_one</code>:
</p>

<p>
	اسم الملف: add_one/Cargo.toml
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5011_20" style=""><span class="pun">[</span><span class="pln">dependencies</span><span class="pun">]</span><span class="pln">
rand </span><span class="pun">=</span><span class="pln"> </span><span class="str">"0.8.5"</span></pre>

<p>
	يمكننا الآن إضافة <code>use rand;‎</code> إلى الملف add_one/src/lib.rs، وبناء كامل مساحة العمل عن طريق تنفيذ <code>cargo build</code> في المجلد "add" الذي سيجلب ويصرِّف الحزمة المصرفة <code>rand</code>. نحصل على تحذير واحد لأننا لم نُشر إلى حزمة <code>rand</code> التي أضفناها إلى النطاق:
</p>

<pre class="ipsCode">$ cargo build
    Updating crates.io index
  Downloaded rand v0.8.5
   --snip--
   Compiling rand v0.8.5
   Compiling add_one v0.1.0 (file:///projects/add/add_one)
warning: unused import: `rand`
 --&gt; add_one/src/lib.rs:1:5
  |
1 | use rand;
  |     ^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

warning: `add_one` (lib) generated 1 warning
   Compiling adder v0.1.0 (file:///projects/add/adder)
    Finished dev [unoptimized + debuginfo] target(s) in 10.18s
</pre>

<p>
	يحتوي ملف Cargo.lock في المستوى العلوي على معلومات عن الاعتمادية لكل من <code>add_one</code> و <code>rand</code>، ولكن وعلى الرغم من أننا نستخدم <code>rand</code> في مكان ما ضمن مساحة العمل إلا أننا لا نستطيع استخدامها في الحزم المصرفة الأخرى إلا إذا اضفنا <code>rand</code> إلى ملف Cargo.toml الخاص بها أيضاً. على سبيل المثال إذا أضفنا <code>use rand;‎</code> إلى ملف adder/scr/main.rs من أجل الحزمة <code>adder</code> سنحصل على خطأ:
</p>

<pre class="ipsCode">$ cargo build
  --snip--
   Compiling adder v0.1.0 (file:///projects/add/adder)
error[E0432]: unresolved import `rand`
 --&gt; adder/src/main.rs:2:5
  |
2 | use rand;
  |     ^^^^ no external crate `rand`
</pre>

<p>
	لحل هذه المشكلة، عدِّل ملف Cargo.toml لحزمة <code>adder</code> وأشِر إلى أن <code>rand</code> هي اعتمادية لها أيضاً. بناء الحزمة <code>adder</code> سيضيف <code>rand</code> إلى لائحة اعتماديات <code>adder</code> في ملف cargo.lock، ولكن لن يجري أي تنزيل لنسخ إضافية من <code>rand</code>. يضمن كارجو أن كل حزمة مصرفة في كل حزمة في مساحة العمل تستخدم نفس الإصدار من الحزمة <code>rand</code>، وبالتالي ستقل المساحة التخزينية المستخدمة وسنضمن أن كل الحزم المصرفة في مساحة العمل ستكون متوافقة مع بعضها بعضًا.
</p>

<h3>
	إضافة اختبار إلى مساحة العمل
</h3>

<p>
	سنضيف اختبارًا للدالة <code>add_one::add_one</code> داخل الحزمة المصرفة <code>add_one</code> للمزيد من التحسينات:
</p>

<p>
	اسم الملف: add_one/src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5011_22" style=""><span class="pln">pub fn add_one</span><span class="pun">(</span><span class="pln">x</span><span class="pun">:</span><span class="pln"> i32</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> i32 </span><span class="pun">{</span><span class="pln">
    x </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">#[cfg(test)]</span><span class="pln">
mod tests </span><span class="pun">{</span><span class="pln">
    use super</span><span class="pun">::*;</span><span class="pln">

    </span><span class="com">#[test]</span><span class="pln">
    fn it_works</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        assert_eq</span><span class="pun">!(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> add_one</span><span class="pun">(</span><span class="lit">2</span><span class="pun">));</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نفّذ الأمر <code>cargo test</code> ضمن مجلد "add" العلوي، إذ سيؤدي تنفيذ <code>cargo test</code> في مساحة عمل مهيكلة بهذا الشكل إلى تنفيذ الاختبارات الخاصة بالحزم المصرفة في مساحة العمل:
</p>

<pre class="ipsCode">$ cargo test
   Compiling add_one v0.1.0 (file:///projects/add/add_one)
   Compiling adder v0.1.0 (file:///projects/add/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.27s
     Running unittests src/lib.rs (target/debug/deps/add_one-f0253159197f7841)

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running unittests src/main.rs (target/debug/deps/adder-49979ff40686fa8e)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests add_one

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
</pre>

<p>
	يُظهر أول قسم في الخرج نجاح اختبار <code>it_works</code> في الحزمة المصرفة <code>add_one</code>، بينما يظهر القسم الثاني أنه لم يُعثر على أي اختبار في الحزمة المصرفة <code>adder</code>، ويظهر القسم الأخير عدم العثور على اختبارات توثيق documentation tests في الحزمة المصرفة <code>add_one</code>.
</p>

<p>
	يمكن أيضاّ تنفيذ اختبارات لحزمة مصرفة محددة في مساحة عمل من المجلد العلوي باستخدام الراية <code>‎-p</code> وتحديد اسم الحزمة المصرفة المراد اختبارها:
</p>

<pre class="ipsCode">$ cargo test -p add_one
    Finished test [unoptimized + debuginfo] target(s) in 0.00s
     Running unittests src/lib.rs (target/debug/deps/add_one-b3235fea9a156f74)

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests add_one

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
</pre>

<p>
	يظهر الخرج أن <code>cargo test</code> نفّذ فقط الاختبارات الموجودة في الحزمة المصرفة <code>add_one</code> ولم ينفّذ الاختبارات الموجودة في الحزمة المصرفة <code>adder</code>.
</p>

<p>
	إذا أردت نشر الحزم المصرفة في مساحة العمل على <a href="https://crates.io/" rel="external nofollow">crates.io</a>، فيجب على كل حزمة مصرفة في مساحة العمل أن تُنشر على حدة. نستطيع نشر حزمة مصرفة معينة في مساحة العمل باستخدام الراية <code>‎-p</code> وتحديد اسم الحزمة المصرفة المراد نشرها بصورةٍ مماثلة للأمر <code>cargo test.</code>
</p>

<p>
	للتدرّب على العملية بصورةٍ أفضل، ضِف الحزمة المصرفة <code>add_two</code> لمساحة العمل هذه بنفس طريقة الحزمة المصرفة <code>add_one</code>.
</p>

<p>
	ضع في الحسبان استخدام مساحة العمل كلما كبر مشروعك، فمن الأسهل فهم مكونات صغيرة ومنفردة على كتلة كبيرة من الشيفرة البرمجية. إضافةً إلى ذلك، إبقاء الحزم المصرفة في مساحة عمل واحدة يجعل التنسيق بين الحزم المصرفة أسهل إذا كانت تُعدَّل باستمرار في نفس الوقت.
</p>

<h2>
	تثبيت الملفات الثنائية binaries باستخدام cargo install
</h2>

<p>
	يسمح لك أمر <code>cargo install</code> بتثبيت واستخدام الوحدات الثنائية المصرفة محليًا، وليس المقصود من ذلك استبدال حزم النظام، إذ أن الأمر موجود ليكون بمثابة طريقة ملائمة لمطوري رست لتثبيت الأدوات التي شاركها الآخرون على crates.io. لاحظ أنه يمكنك فقط تثبيت الحزم التي تحتوي أهداف ثنائية binary targets، والهدف الثنائي هو برنامج قابل للتشغيل يُنشأ إذا كانت الحزمة المصرفة تحتوي على ملف src/main.rs أو ملف آخر محدد على أنه ملف تنفيذي، على عكس هدف المكتبة library target الذي لا يمكن تشغيله لوحده، فهو موجود لضمِّه داخل برامج أخرى. تحتوي الحزم المصرفة عادةً على معلومات في ملف README وتدل هذه المعلومات فيما إذا كانت الوحدة المصرفة مكتبية أو تحتوي هدفًا ثنائيًا أو كلاهما.
</p>

<p>
	تُخزَّن كل الوحدات الثنائية المصرفة المثبتة عند تنفيذ <code>cargo install</code> في مجلد التثبيت الجذر الذي يدعى "bin". إذا ثبتّت رست باستخدام "rustup.rs" ولم يكن لديك أي إعدادات افتراضية فإن المجلد سيكون ‎$HOME/.cargo/bin. تأكد أن هذا المجلد في <code>‎$PATH</code> الخاص بك لتكون قادراً على تشغيل البرامج التي ثبتتها باستخدام <code>cargo install</code>.
</p>

<p>
	ذكرنا سابقًا أن هناك تنفيذ لأداة <code>grep</code> بلغة رست اسمه <code>ripgrep</code> للبحث عن الملفات، ولتثبيت <code>ripgrep</code> يمكنك تنفيذ الأمر التالي:
</p>

<pre class="ipsCode">$ cargo install ripgrep
    Updating crates.io index
  Downloaded ripgrep v13.0.0
  Downloaded 1 crate (243.3 KB) in 0.88s
  Installing ripgrep v13.0.0
--snip--
   Compiling ripgrep v13.0.0
    Finished release [optimized + debuginfo] target(s) in 3m 10s
  Installing ~/.cargo/bin/rg
   Installed package `ripgrep v13.0.0` (executable `rg`)
</pre>

<p>
	يظهر السطر الثاني قبل الأخير من المخرجات مكان واسم الثنائية المثبتة، وهي <code>rg</code> في حالة <code>ripgrep</code>. إذا كان مجلد التثبيت موجودًا في <code>‎$PATH</code> الخاص بك، فيمكنك تشغيل <code>rg --help</code> والبدء باستخدام أداة أسرع مكتوبة بلغة رست للبحث عن الملفات.
</p>

<h2>
	توسيع استخدامات كارجو عن طريق أوامر مخصصة
</h2>

<p>
	كارجو مصمم بحيث يمكن توسيع استخداماته بأوامر فرعية دون الحاجة لتعديله. إذا كان هناك وحدة ثنائية binary ضمن <code>‎$PATH</code> اسمها <code>cargo-something</code>، فهذا يعني أنه يمكنك تشغيلها كما لو كانت أمر فرعي لكارجو عن طريق تنفيذ <code>cargo something</code>. تستطيع استعراض الأوامر المخصصة بتنفيذ <code>cargo --list</code>. قدرتك على استخدام <code>cargo install</code> لتثبيت الإضافات وتشغيلها كما في أدوات الكارجو المضمّنة built-in هي ميزة ملائمة جداً بتصميم كارجو.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch14-00-more-about-cargo.html" rel="external nofollow">More About Cargo and Crates.io</a> من كتاب <a href="https://doc.rust-lang.org/stable/book/title-page.html/" rel="external nofollow">The Rust Programming Language</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B0%D9%83%D9%8A%D8%A9-smart-pointers-%D9%81%D9%8A-%D8%B1%D8%B3%D8%AA-rust-r2021/" rel="">المؤشرات الذكية Smart Pointers في رست Rust</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D8%AE%D8%B5%D9%8A%D8%B5-%D9%86%D8%B3%D8%AE-%D9%85%D8%B4%D8%B1%D9%88%D8%B9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D9%88%D9%86%D8%B4%D8%B1%D9%87%D8%A7-%D8%B9%D9%84%D9%89-cratesio-r2013/" rel="">تخصيص نسخ مشروع بلغة رست ونشرها على crates.io</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1920/" rel="">الأخطاء والتعامل معها في لغة رست</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D9%84%D9%83%D9%8A%D8%A9-ownership-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1786/" rel="">الملكية Ownership في لغة رست</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2014</guid><pubDate>Fri, 30 Jun 2023 13:00:00 +0000</pubDate></item><item><title>&#x62A;&#x62E;&#x635;&#x64A;&#x635; &#x646;&#x633;&#x62E; &#x645;&#x634;&#x631;&#x648;&#x639; &#x628;&#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A; &#x648;&#x646;&#x634;&#x631;&#x647;&#x627; &#x639;&#x644;&#x649; crates.io</title><link>https://academy.hsoub.com/programming/rust/%D8%AA%D8%AE%D8%B5%D9%8A%D8%B5-%D9%86%D8%B3%D8%AE-%D9%85%D8%B4%D8%B1%D9%88%D8%B9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D9%88%D9%86%D8%B4%D8%B1%D9%87%D8%A7-%D8%B9%D9%84%D9%89-cratesio-r2013/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_07/----Rust----release-profiles---.png.0851b3a7fd85938061ad41ab0e6009cf.png" /></p>
<p>
	سنتعرّف هنا على كيفية تخصيص نُسَخ المشروع builds المختلفة باستخدام ما يُدعى حسابات الإصدار release profiles، ثمّ سنستعرض كيفيّة إضافة مشاريعك بلغة رست على موقع crates.io.
</p>

<h2>
	تخصيص نسخ مشروع مع حسابات الإصدار وضبطها في كارجو cargo
</h2>

<p>
	حسابات الإصدار في رست هي حسابات قابلة للتعديل customizable ومعرّفة مسبقًا بالعديد من الإعدادات المختلفة التي تسمح للمبرمج بأن يمتلك تحكمًا أكبر على العديد من الخيارات لتصريف الشيفرة البرمجية، إذ أن كل حساب مضبوط بصورةٍ مستقلة عن الآخر.
</p>

<p>
	لدى كارجو حسابين أساسيين، هما: حساب <code>dev</code> وهو حساب يستخدمه كارجو عندما تنفّذ <code>cargo build</code> وحساب <code>release</code> يستخدمه كارجو عندما تنفّذ <code>cargo build --release</code>. حساب <code>dev</code> معرَّف بإعدادات تصلح لعملية التطوير، وحساب <code>release</code> مُعرَّف بإعدادت تصلح لإطلاق نسخ جديدة.
</p>

<p>
	قد تكون أسماء الحسابات هذه ضمن خرج نسخ مشروعك مألوفة:
</p>

<pre class="ipsCode">$ cargo build
    Finished dev [unoptimized + debuginfo] target(s) in 0.0s
$ cargo build --release
    Finished release [optimized] target(s) in 0.0s
</pre>

<p>
	حسابات <code>dev</code> و <code>release</code> هي الحسابات التي يستخدمها المصرّف.
</p>

<p>
	يمتلك كارجو إعدادات افتراضية لكلٍ من الحسابات التي تُطبَّق عندما لا تُحدد بوضوح بإضافة أية أقسام <code>[profile.*‎]</code> إلى ملف cargo.toml الخاص بالمشروع، إذ عند إضافة قسم <code>[profile.*‎]</code> لأي حساب تريد التعديل عليه، فأنت تُعيد الكتابة على أي من الإعدادات الفرعية الخاصة بالإعدادات الافتراضية. مثلاً، هذه هي القيم الأساسية لإعدادات <code>opt-level</code> لكل من الحسابين <code>dev</code> و <code>release</code>.
</p>

<p>
	اسم الملف: cargo.toml
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2197_9" style=""><span class="pun">[</span><span class="pln">profile</span><span class="pun">.</span><span class="pln">dev</span><span class="pun">]</span><span class="pln">
opt</span><span class="pun">-</span><span class="pln">level </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">

</span><span class="pun">[</span><span class="pln">profile</span><span class="pun">.</span><span class="pln">release</span><span class="pun">]</span><span class="pln">
opt</span><span class="pun">-</span><span class="pln">level </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span></pre>

<p>
	تتحكم إعدادات <code>opt-level</code> بعدد التحسينات التي ستطبقها رست على شيفرتك البرمجية، بمجال من 0 إلى 3، مع الانتباه إلى أن تطبيق أي تحسينات إضافية سيزيد من وقت التصريف، لذا إذا كنت في مرحلة التطوير وتصرّف شيفرتك البرمجية بصورةٍ متكررة، فستحتاج إلى تحسينات أقل لتُصرف أسرع حتى لو كانت الشيفرة البرمجية الناتجة تعمل أبطأ. ستكون القيمة لكلٍ من <code>opt-level</code> و <code>dev</code> افتراضيًا هي 0، وعندما تكون جاهزاً لإصدار شيفرتك البرمجية، فمن الأفضل أن تقضي وقتاً أكثر بالتصريف إذ أنك ستصرّف الشيفرة البرمجية لمرة واحدة فقط في وضع الإصدار، لكنك ستشغّل البرنامج عدّة مرات، إذًا يُقايض وضع الإصدار وقت التصريف الطويل مقابل شيفرة برمجية تعمل على نحوٍ أسرع، وهذا هو السبب في كون القيمة الأساسية <code>opt-level</code> للحساب <code>release</code> هي 3.
</p>

<p>
	يمكنك تجاوز القيمة الافتراضية بإضافة قيمة مختلفة لها في Cargo.toml، فإذا أردنا مثلًا أن يكون مستوى التحسين هو 1 في حساب التطوير، يمكننا إضافة هذين السطرين في ملف Cargo.toml الخاص بالمشروع:
</p>

<p>
	اسم الملف: Cargo.toml
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2197_11" style=""><span class="pun">[</span><span class="pln">profile</span><span class="pun">.</span><span class="pln">dev</span><span class="pun">]</span><span class="pln">
opt</span><span class="pun">-</span><span class="pln">level </span><span class="pun">=</span><span class="lit">1</span></pre>

<p>
	تعيد هذه الشيفرة تعريف الإعداد الافتراضي 0، وعندما ننفّذ <code>cargo build</code>، سيستخدم كارجو الإعدادات الافتراضية لحساب <code>dev</code> إضافةً إلى التعديلات التي أجريناها على <code>opt-level</code>، ولأننا ضبطنا <code>opt-level</code> إلى القيمة 1، سيطبّق كارجو تحسينات أكثر من التحسينات التي يطبقها في الوضع الافتراضي، ولكن ليس أكثر من التحسينات الموجودة في إصدار البناء.
</p>

<p>
	للحصول على لائحة كاملة من خيارات الضبط والقيم الافتراضية لكل حساب راجع <a href="https://doc.rust-lang.org/cargo/reference/profiles.html" rel="external nofollow">توثيق كارجو</a>.
</p>

<h2>
	نشر وحدة مصرفة crate على crates.io
</h2>

<p>
	استخدمنا حزم من <a href="https://crates.io/" rel="external nofollow">crates.io</a> مثل اعتماديات dependencies لمشاريعنا، لكن يمكنك أيضًا أن تشارك شيفرتك مع أشخاص آخرين عن طريق نشر حزمتك الخاصة. يوزِّع تسجيل <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AD%D8%B2%D9%85-packages-%D9%88%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B5%D8%B1%D9%81%D8%A9-crates-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1853/" rel="">الوحدة المُصرَّفة crates</a> في crates.io الشيفرة المصدرية للحزم الخاصة بك، بحيث تكون الشيفرة المُستضافة مفتوحة المصدر.
</p>

<p>
	لدى رست وكارجو ميزات تجعل حزمتك المنشورة أسهل إيجادًا من قبل الأشخاص واستخدامها وسنتحدث عن بعض هذه المزايا ثم سنشرح كيف تنشر حزمة.
</p>

<h2>
	كتابة تعليقات توثيق مفيدة
</h2>

<p>
	سيساعد توثيق حزمتك بدقة المستخدمين الآخرين على معرفة كيف ومتى يستخدمونها، لذا يُعد استثمار الوقت في كتابة التوثيق أمرًا مجديًا. ناقشنا سابقًا <a href="https://academy.hsoub.com/programming/rust/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-functions-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%82%D8%A7%D8%AA-comments-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%A7%D8%B3%D8%AA-rust-r1781/" rel="">كيف تعلّق شيفرة رست</a> باستخدام خطين مائلين "//". لدى رست أيضًا نوع محدد من التعليقات للتوثيق تعرف بتعليق التوثيق documentation comment والذي سيولّد بدوره توثيق HTML. يعرض الملف المكتوب باستخدام HTML محتويات تعليقات التوثيق لعناصر <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D9%87%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%84%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-api%D8%9F-r1512/" rel="">الواجهة البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> العامة والموجهة للمبرمجين المهتمين بمعرفة كيفية استخدام وحدة مصرفة بغض النظر عن كيفية عملها وراء الكواليس.
</p>

<p>
	تستخدم تعليقات التوثيق ثلاثة خطوط مائلة "///" بدلاً من خطّين، وتدعم <a href="https://academy.hsoub.com/apps/productivity/%D9%83%D9%8A%D9%81-%D8%AA%D9%83%D8%AA%D8%A8-%D8%A8%D8%B5%D9%8A%D8%BA%D8%A9-%D9%85%D8%A7%D8%B1%D9%83%D8%AF%D8%A7%D9%88%D9%86-%D8%A8%D8%A8%D8%B3%D8%A7%D8%B7%D8%A9-r290/" rel="">صيغة ماركداون Markdown</a> لتنسيق النص، إذ يكفي وضع تعليقات النص مباشرةً قبل العنصر الذي يوَثّق. تُظهر الشيفرة 1 تعليقات التوثيق لدالة <code>add_one</code> في وحدة مصرفة تدعى <code>my_crate</code>.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2197_13" style=""><span class="com">/// أضف واحد إلى الرقم المُعطى</span><span class="pln">
</span><span class="com">///</span><span class="pln">
</span><span class="com">/// # أمثلة</span><span class="pln">
</span><span class="com">///</span><span class="pln">
</span><span class="com">/// ```</span><span class="pln">
</span><span class="com">/// let arg = 5;</span><span class="pln">
</span><span class="com">/// let answer = my_crate::add_one(arg);</span><span class="pln">
</span><span class="com">///</span><span class="pln">
</span><span class="com">/// assert_eq!(6, answer);</span><span class="pln">
</span><span class="com">/// ```</span><span class="pln">
pub fn add_one</span><span class="pun">(</span><span class="pln">x</span><span class="pun">:</span><span class="pln"> i32</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> i32 </span><span class="pun">{</span><span class="pln">
    x </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 1- تعليق التوثيق لدالة]
</p>

<p>
	نقدم هنا وصفًا عما تفعله دالة <code>add_one</code>، ابدأ القسم بعنوان <code>Examples</code> ثم ضع شيفرة تعبّر عن كيفية استخدام الدالة <code>add_one</code>، ويمكننا توليد توثيق HTML من تعليق التوثيق هذا عن طريق تنفيذ <code>cargo doc</code>، إذ يشغّل هذا الأمر أداة <code>rustdoc</code> الموزّعة مع رست وتضع توثيق HTML المولّد في المجلد "target/doc".
</p>

<p>
	سيبني تنفيذ <code>cargo doc --open</code> ملف HTML للتوثيق الحالي لوحدتك المصرفة (وأيضًا توثيق كل ما يعتمد على وحدتك المصرفة) ويفتح النتيجة في متصفح ويب للسهولة. انتقل إلى الدالة <code>add_one</code> وسترى كيف يتحول النص في تعليقات التوثيق، كما يظهر في الشكل 1:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="129662" href="https://academy.hsoub.com/uploads/monthly_2023_07/14-01.png.fc6424db418be6c9be1d1492cbe65136.png" rel=""><img alt="14-01.png" class="ipsImage ipsImage_thumbnailed" data-fileid="129662" data-unique="u1lk3i06r" src="https://academy.hsoub.com/uploads/monthly_2023_07/14-01.thumb.png.24d6958573a8052cbdf140ccdc9324b3.png"> </a>
</p>

<p style="text-align: center;">
	[الشكل 1: توثيق HTML لدالة <code>add_one</code>]
</p>

<h3>
	الأقسام شائعة الاستخدام
</h3>

<p>
	استخدمنا عنوان ماركداون <code>‎# Examples</code> في الشيفرة 1 لإنشاء قسم في ملف HTML بالعنوان "Examples" وهذه بعض الأجزاء الشائعة الأخرى التي يستخدمها مؤلفو الوحدات المصرفة في توثيقهم:
</p>

<ul>
	<li>
		<strong>الهلع Panics</strong>: السيناريوهات التي تتوقف بها الدوال المُضمنة بالتوثيق، ويجب على مستخدمي الدالة الذين لا يريدون لبرامجهم أن تتوقف ألا يستدعوا الدالة في هذه الحالات.
	</li>
	<li>
		<strong>الأخطاء Errors</strong>: إذا أعادت الدالة القيمة <code>Result</code> واصفةً أنواع الأخطاء التي قد حصلت للشيفرة البرمجية المُستدعاة والظروف التي قد تسببت بحدوث الأخطاء التي تعيدها، تكون هذه المعلومات مفيدة للمستخدمين ويمكنهم بهذا أن يكتبوا شيفرة تستطيع التعامل مع الأنواع المختلفة من الأخطاء بعدة طرق.
	</li>
	<li>
		<strong>الأمان Safety</strong>: إذا استُدعيَت الدالة <code>unsafe</code> (نناقش عدم الأمان Unsafety لاحقًا)، يجب أن يكون هناك قسمٌ يشرح سبب عدم أمان الدالة ويغطي الأنواع اللا متغايرة invariants التي تتوقعها الدالة من المستخدمين.
	</li>
</ul>

<p>
	لا تحتاج معظم تعليقات التوثيق لكل هذه الأقسام، لكن هذه لائحة جيدة تذكرك بالجوانب التي سيهتم مستخدمو شيفرتك البرمجية بمعرفتها.
</p>

<h3>
	استخدام تعليقات التوثيق مثل اختبارات
</h3>

<p>
	يساعد إضافة كُتَل من الشيفرات البرمجية على أنها مثال ضمن تعليقات التوثيق فهم كيفية استخدام مكتبتك، ولفعل هذا الأمر مزايا إضافية: إذ أن تنفيذ <code>cargo test</code> سينفذ بدوره أمثلة الشيفرة البرمجية في توثيقك مثل اختبارات. لا شيء أفضل من توثيق يحتوي على أمثلة، لكن لا شيء أسوأ من أمثلة لا تعمل لأن الشيفرة البرمجية قد تغيرت منذ وقت كتابة التوثيق. نحصل على قسم في نتائج الاختبارات إذا نفّذنا الأمر <code>cargo test</code> مع توثيق دالة <code>add_one</code> من الشيفرة 1 على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2197_15" style=""><span class="pln">      </span><span class="typ">Doc</span><span class="pun">-</span><span class="pln">tests my_crate

running </span><span class="lit">1</span><span class="pln"> test
test src</span><span class="pun">/</span><span class="pln">lib</span><span class="pun">.</span><span class="pln">rs </span><span class="pun">-</span><span class="pln"> add_one </span><span class="pun">(</span><span class="pln">line </span><span class="lit">5</span><span class="pun">)</span><span class="pln"> </span><span class="pun">...</span><span class="pln"> ok

test result</span><span class="pun">:</span><span class="pln"> ok</span><span class="pun">.</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> passed</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> failed</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> ignored</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> measured</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> filtered out</span><span class="pun">;</span><span class="pln"> finished in </span><span class="lit">0.27s</span></pre>

<p>
	الآن إذا غيرنا إما الدالة أو المثال بحيث يهلع الماكرو <code>assert_eq!‎</code> في المثال وينفّذ <code>cargo test</code> مرةً أخرى، سنرى أن اختبارات التوثيق تلاحظ أن شيفرة المثال والشيفرة البرمجية لا يتزامنان مع بعضهما بعضًا.
</p>

<h3>
	تعليق العناصر المحتواة
</h3>

<p>
	يضيف أسلوب التعليق "!//" التوثيق إلى العنصر الذي يحتوي على التعليقات بدلاً من العناصر التي تلي التعليقات، ونستخدم عادةً تعليقات المستند هذه داخل ملف الوحدة المصرفة الجذر (src/lib.rs اصطلاحًا)، أو داخل وحدة ما لتوثيق الوحدة المصرفة، أو الوحدة ككُل. على سبيل المثال، لإضافة التوثيق التي يصف الغرض من الوحدة المصرّفة <code>my_crate</code> التي تحتوي على الدالة <code>add_one</code>، نضيف تعليقات التوثيق التي تبدأ بـ "!//" إلى بداية الملف src/lib.rs، كما هو موضح في الشيفرة 2:
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2197_17" style=""><span class="com">//! # My Crate</span><span class="pln">
</span><span class="com">//!</span><span class="pln">
</span><span class="com">//! `my_crate` is a collection of utilities to make performing certain</span><span class="pln">
</span><span class="com">//! calculations more convenient.</span><span class="pln">

</span><span class="com">/// Adds one to the number given.</span><span class="pln">
</span><span class="com">// --snip--</span></pre>

<p style="text-align: center;">
	[الشيفرة 2: توثيق الوحدة المصرفة <code>my_crate</code> ككل]
</p>

<p>
	لاحظ عدم وجود أي شيفرة برمجية بعد آخر سطر يبدأ بـ "!//"، وذلك لأننا بدأنا التعليقات بـ "!//" بدلاً من "///". نوثّق العنصر الذي يحتوي على هذا التعليق بدلاً من العنصر الذي يتبع هذا التعليق، وفي هذه الحالة، يكون هذا العنصر هو ملف src/lib.rs، وهو الوحدة المصرفة الجذر، إذ تصف هذه التعليقات كامل الوحدة المصرفة.
</p>

<p>
	عندما ننفّذ <code>cargo doc --open</code>، تظهر هذه التعليقات على الصفحة الأولى من توثيق <code>my_crate</code> أعلى قائمة العناصر العامة في الوحدة المصرفة، كما هو موضح في الشكل 2:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="129661" href="https://academy.hsoub.com/uploads/monthly_2023_07/14-02.png.f80099ce4a1413522de567ea3adc298f.png" rel=""><img alt="14-02.png" class="ipsImage ipsImage_thumbnailed" data-fileid="129661" data-unique="otjx037pz" src="https://academy.hsoub.com/uploads/monthly_2023_07/14-02.thumb.png.37c6e3efc2fc341d29302ac93a924437.png"> </a>
</p>

<p style="text-align: center;">
	[الشكل 2: التوثيق المولَّد للوحدة المصرّفة <code>my_crate</code> متضمنًا التعليق الذي يصف كل الوحدة المصرفة]
</p>

<p>
	تُعد تعليقات التوثيق داخل العناصر مفيدةً لوصف الوحدات المصرفة والوحدات خصوصًا. استخدمها لشرح الغرض العام من الحاوية container لمساعدة المستخدمين على فهم تنظيم الوحدة المصرفة.
</p>

<h2>
	تصدير واجهة برمجية عامة Public <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> ملائمة باستخدام pub use
</h2>

<p>
	يُعد هيكل الواجهة البرمجية العامة أحد النقاط المهمة عند نشر وحدة مصرفة، إذ يكون الأشخاص الذين يستخدمون الوحدة المصرفة الخاصة بك أقل دراية منك بهيكلية الوحدة، وقد يواجهون صعوبةً في إيجاد الأجزاء التي يريدون استخدامها إذا كانت الوحدة المصرفة الخاصة بك تحتوي على تسلسل هرمي كبير.
</p>

<p>
	تناولنا سابقًا كيفية جعل العناصر عامة باستخدام الكلمة المفتاحية <code>pub</code>، وإضافة العناصر إلى نطاق باستخدام الكلمة المفتاحية <code>use</code>. ومع ذلك، قد لا تكون الهيكلية المنطقية بالنسبة لك أثناء تطوير الوحدة المصرفة مناسبةً للمستخدمين، إذ قد ترغب في تنظيم الهياكل الخاصة بك ضمن تسلسل هرمي يحتوي على مستويات متعددة، ولكن قد يواجه بعض الأشخاص مشاكلًا بخصوص وجود نوع ما عرّفته في مكان عميق ضمن التسلسل الهرمي وذلك عندما يرغبون باستخدامه، كما قد يسبب الاضطرار إلى إدخال المسار التالي بعض الازعاج:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4121_7" style=""><span class="pln">use my_crate</span><span class="pun">::</span><span class="pln">some_module</span><span class="pun">::</span><span class="pln">another_module</span><span class="pun">::</span><span class="typ">UsefulType</span><span class="pun">;‎</span></pre>

<p>
	بدلًا من استخدام:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4121_9" style=""><span class="pln">use my_crate</span><span class="pun">::</span><span class="typ">UsefulType</span><span class="pun">;‎</span></pre>

<p>
	الخبر السار هو أنه إذا لم يكن الهيكل مناسبًا للآخرين لاستخدامه من مكتبة أخرى، فلن تضطر إلى إعادة ترتيب التنظيم الداخلي، إذ يمكنك إعادة تصدير العناصر بدلًا من ذلك لإنشاء هيكل عام مختلف عن هيكلتك الخاصة باستخدام <code>pub use</code>. تأخذ عملية إعادة التصدير عنصرًا عامًا من مكان ما وتجعله عامًا في مكان آخر، كما لو جرى تعريفه في موقع آخر عوضًا عن ذلك.
</p>

<p>
	على سبيل المثال، لنفترض أننا أنشأنا مكتبة باسم <code>art</code> لنمذجة المفاهيم الفنية، بحيث يوجد داخل هذه المكتبة وحدتان: وحدة <code>kinds</code> تحتوي على معدّدَين enums باسم <code>PrimaryColor</code> و <code>SecondaryColor</code> ووحدة <code>utils</code> تحتوي على دالة تدعى <code>mix</code>، كما هو موضح في الشيفرة 3:
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2197_19" style=""><span class="com">//! # Art</span><span class="pln">
</span><span class="com">//!</span><span class="pln">
</span><span class="com">//! مكتبة لنمذجة المفاهيم الفنية</span><span class="pln">

pub mod kinds </span><span class="pun">{</span><span class="pln">
    </span><span class="com">/// الألوان الأساسية طبقًا لنموذج‪ ‪‪‪RYB  </span><span class="pln">
    pub </span><span class="kwd">enum</span><span class="pln"> </span><span class="typ">PrimaryColor</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Red</span><span class="pun">,</span><span class="pln">
        </span><span class="typ">Yellow</span><span class="pun">,</span><span class="pln">
        </span><span class="typ">Blue</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="com">/// الألوان الثانوية طبقًا لنموذج‪ RYB</span><span class="pln">
    pub </span><span class="kwd">enum</span><span class="pln"> </span><span class="typ">SecondaryColor</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Orange</span><span class="pun">,</span><span class="pln">
        </span><span class="typ">Green</span><span class="pun">,</span><span class="pln">
        </span><span class="typ">Purple</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

pub mod utils </span><span class="pun">{</span><span class="pln">
    use crate</span><span class="pun">::</span><span class="pln">kinds</span><span class="pun">::*;</span><span class="pln">

    </span><span class="com">/// دمج لونين أساسيين بقيم متساوية لإنشاء لون ثانوي</span><span class="pln">
    pub fn mix</span><span class="pun">(</span><span class="pln">c1</span><span class="pun">:</span><span class="pln"> </span><span class="typ">PrimaryColor</span><span class="pun">,</span><span class="pln"> c2</span><span class="pun">:</span><span class="pln"> </span><span class="typ">PrimaryColor</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">SecondaryColor</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// --snip--</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 3: مكتبة <code>art</code> التي تحتوي على عناصر منظمة ضمن الوحدتين <code>kinds</code> و <code>utils</code>]
</p>

<p>
	يوضح الشكل 3 كيف ستبدو الصفحة الأولى لتوثيق الوحدة المصرفة التي أُنشئت بواسطة <code>cargo doc</code>:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="129660" href="https://academy.hsoub.com/uploads/monthly_2023_07/14-03.png.a6c41393fe4e1b7ee8fe067e2337eb38.png" rel=""><img alt="14-03.png" class="ipsImage ipsImage_thumbnailed" data-fileid="129660" data-unique="fiizq4rns" src="https://academy.hsoub.com/uploads/monthly_2023_07/14-03.thumb.png.a0bc1fc0231194dfa874ce435c2ace55.png"> </a>
</p>

<p style="text-align: center;">
	[الشكل 3: الصفحة الأولى لتوثيق <code>art</code> الذي توضّح الوحدتين <code>kinds</code> و <code>utils</code>]
</p>

<p>
	لاحظ أن النوعين <code>PrimaryColor</code> و <code>SecondaryColor</code> غير مُدرجين في الصفحة الأولى وكذلك دالة <code>mix</code>، إذ يجب علينا النقر على <code>kinds</code> و <code>utils</code> لرؤيتهما.
</p>

<p>
	ستحتاج وحدة مصرفة أخرى تعتمد على هذه المكتبة إلى عبارات <code>use</code>، لتجلب العناصر الموجودة في <code>art</code> إلى النطاق، وبالتالي تحديد هيكل الوحدة المعرّفة حاليًا. تُظهر الشيفرة 4 مثالاً على الوحدة المصرفة التي تستخدم عناصر <code>PrimaryColor</code> و <code>mix</code> من الوحدة المصرفة <code>art</code>:
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2197_21" style=""><span class="pln">use art</span><span class="pun">::</span><span class="pln">kinds</span><span class="pun">::</span><span class="typ">PrimaryColor</span><span class="pun">;</span><span class="pln">
use art</span><span class="pun">::</span><span class="pln">utils</span><span class="pun">::</span><span class="pln">mix</span><span class="pun">;</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let red </span><span class="pun">=</span><span class="pln"> </span><span class="typ">PrimaryColor</span><span class="pun">::</span><span class="typ">Red</span><span class="pun">;</span><span class="pln">
    let yellow </span><span class="pun">=</span><span class="pln"> </span><span class="typ">PrimaryColor</span><span class="pun">::</span><span class="typ">Yellow</span><span class="pun">;</span><span class="pln">
    mix</span><span class="pun">(</span><span class="pln">red</span><span class="pun">,</span><span class="pln"> yellow</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 4: وحدة مصرفة تستخدم عناصر الوحدة المصرفة <code>art</code> مع تصدير هيكلها الداخلي]
</p>

<p>
	ينبغي على مؤلف الشيفرة في الشيفرة 4 التي تستخدم الوحدة المصرفة <code>art</code> أن يعرّف أن <code>PrimaryColor</code> موجود في الوحدة <code>kinds</code> وأن <code>mix</code> موجودة في الوحدة <code>utils</code>. هيكل الوحدة المصرفة <code>art</code> مناسب أكثر للمطورين العاملين على الوحدة المصرفة <code>art</code> مقارنةً بمن سيستخدمها، إذ لا يحتوي الهيكل الداخلي على أي معلومات مفيدة لشخص يحاول فهم كيفية استخدام الوحدة المصرفة <code>art</code>، بل يتسبب الهيكل الداخلي باللّبس لأن المطورين الذين يستخدمونها يجب أن يعرفوا أيّ المسارات التي يجب عليهم الذهاب إليها كما يجب عليهم تحديد أسماء الوحدات في عبارات <code>use</code>.
</p>

<p>
	لإزالة التنظيم الداخلي من الواجهة البرمجية العامة يمكننا تعديل شيفرة الوحدة المصرفة <code>art</code> في الشيفرة 3 لإضافة تعليمات <code>pub use</code> لإعادة تصدير العناصر للمستوى العلوي كما هو موضح في الشيفرة 5:
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2197_23" style=""><span class="com">//! # Art</span><span class="pln">
</span><span class="com">//!</span><span class="pln">
</span><span class="com">//! مكتبة لنمذجة المفاهيم الفنية</span><span class="pln">

pub use self</span><span class="pun">::</span><span class="pln">kinds</span><span class="pun">::</span><span class="typ">PrimaryColor</span><span class="pun">;</span><span class="pln">
pub use self</span><span class="pun">::</span><span class="pln">kinds</span><span class="pun">::</span><span class="typ">SecondaryColor</span><span class="pun">;</span><span class="pln">
pub use self</span><span class="pun">::</span><span class="pln">utils</span><span class="pun">::</span><span class="pln">mix</span><span class="pun">;</span><span class="pln">

pub mod kinds </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

pub mod utils </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 5: إضافة تعليمات <code>pub use</code> لإعادة تصدير العناصر]
</p>

<p>
	سيُدرِج توثيق الواجهة البرمجية التي يولدها الأمر <code>cargp doc</code> لهذه الوحدة المصرفة ويعيد تصديرها على الصفحة الأولى كما هو موضح في الشكل 4 جاعلًا النوعَين <code>PrimaryColor</code> و <code>SecondaryColor</code> ودالة <code>mix</code> أسهل للإيجاد.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="129659" href="https://academy.hsoub.com/uploads/monthly_2023_07/14-04.png.87e3c0b14628914cee3142ae76d0de02.png" rel=""><img alt="14-04.png" class="ipsImage ipsImage_thumbnailed" data-fileid="129659" data-unique="cs14o4to8" src="https://academy.hsoub.com/uploads/monthly_2023_07/14-04.thumb.png.533736e3da5351b4ddc5347c6d631a84.png"> </a>
</p>

<p style="text-align: center;">
	[الشكل 4: الصفحة الأولى لتوثيق <code>art</code> التي تعرض عمليات إعادة التصدير]
</p>

<p>
	يمكن لمستخدمي الوحدة المصرفة <code>art</code> أن يروا ويستخدموا الهيكلة الداخلية من الشيفرة 3 كما هو موضح في الشيفرة 4 أو يمكنهم استخدام هيكل أكثر سهولة للاستخدام في الشيفرة 5 كما هو موضح في الشيفرة 6:
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2197_25" style=""><span class="pln">use art</span><span class="pun">::</span><span class="pln">mix</span><span class="pun">;</span><span class="pln">
use art</span><span class="pun">::</span><span class="typ">PrimaryColor</span><span class="pun">;</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 6: برنامج يستخدم العناصر المعاد تصديرها من الوحدة المصرفة <code>art</code>]
</p>

<p>
	يمكن -في الحالات التي يوجد فيها العديد من الوحدات المتداخلة nested modules- أن تحدث عملية إعادة تصدير الأنواع في المستوى العلوي باستخدام <code>pub use</code> فرقًا واضحًا على تجربة الأشخاص في استخدام الوحدة المصرّفة. الاستخدام الشائع الآخر للتعليمة <code>pub use</code> هو إعادة تصدير تعريفات الاعتمادية في الوحدة المصرفة الحالية لجعل تعريفات تلك الوحدة المصرفة جزءًا من الواجهة البرمجية العامة لوحدتك المصرفة.
</p>

<p>
	يُعد إنشاء بنية واجهة برمجية عامة مفيدة فنًا أكثر من كونه علمًا، ويمكنك تكرار المحاولة حتى تعثر على واجهة برمجية تعمل بصورةٍ أفضل لمستخدميها، ويمنحك اختيار <code>pub use</code> مرونةً في كيفية هيكلة وحدتك المصرفة داخليًا وفصل هذه الهيكلة الداخلية عما تقدمه للمستخدمين. ألقِ نظرةً على الشيفرات البرمجية الخاصة ببعض الوحدات المصرفة التي ثبّتتها لمعرفة ما إذا كانت هيكلتها الداخلية مختلفة عن الواجهة البرمجية العامة.
</p>

<h2>
	إنشاء حساب Crates.io
</h2>

<p>
	قبل أن تتمكن من نشر أي وحدات مصرفة، تحتاج إلى إنشاء حساب على <a href="https://crates.io/" rel="external nofollow">crates.io</a> والحصول على رمز واجهة برمجية مميز <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> token، ولفعل ذلك، انقر على زر الصفحة الرئيسية على <a href="https://crates.io/" rel="external nofollow">crates.io</a> وسجّل الدخول عبر حساب غيت هب GitHub، إذ يُعد حساب غيت هب أحد المتطلبات حاليًا، ولكن قد يدعم الموقع طرقًا أخرى لإنشاء حساب في المستقبل. بمجرد تسجيل الدخول، اذهب إلى إعدادات حسابك على <a href="https://crates.io/me" rel="external nofollow">https://crates.io/me</a> واسترجع مفتاح <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>. ثم نفّذ الأمر <code>cargo login</code> باستخدام مفتاح <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> الخاص بك، كما يلي:
</p>

<pre class="ipsCode">$ cargo login abcdefghijklmnopqrstuvwxyz012345
</pre>

<p>
	يُعلم هذا الأمر كارجو برمز <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> الخاص بك وتخزينه محليًا في "‎~/.cargo/credentials". لاحظ أن هذا الرمز هو سر، فلا تشاركه مع أي شخص آخر، وإذا شاركته مع أي شخص لأي سبب من الأسباب، فيجب عليك إبطاله وإنشاء رمز مميز جديد على <a href="https://crates.io/" rel="external nofollow">crates.io</a>.
</p>

<h2>
	إضافة بيانات وصفية لوحدة مصرفة جديدة
</h2>

<p>
	لنفترض أن لديك وحدة مصرفة تريد نشرها، ستحتاج قبل النشر إلى إضافة بعض البيانات الوصفية في قسم <code>[package]</code> داخل ملف Cargo.toml الخاص بالوحدة المصرفة.
</p>

<p>
	ستحتاج وحدتك المصرفة إلى اسم مميز، إذ يُمكنك تسمية الوحدة المصرفة أثناء عملك على وحدة مصرفة محليًا كما تريد، ومع ذلك، تُخصَّص أسماء الوحدات المصرفة على crates.io على أساس من يأتي أولًا يُخدم أولًا first-come, first-served. بمجرد اختيار اسم لوحدة مصرفة ما، لا يمكن لأي شخص آخر نشر وحدة مصرفة بهذا الاسم. قبل محاولة نشر وحدة مصرفة، ابحث عن الاسم الذي تريد استخدامه، فإذا كان الاسم مستخدمًا، ستحتاج إلى البحث عن اسم آخر وتعديل حقل <code>name</code> في ملف Cargo.toml في قسم <code>[package]</code> لاستخدام الاسم الجديد للنشر، كما يلي:
</p>

<p>
	اسم الملف: Cargo.toml
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2197_27" style=""><span class="pun">[</span><span class="pln">package</span><span class="pun">]</span><span class="pln">
name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"guessing_game"</span></pre>

<p>
	حتى إذا اخترت اسمًا مميزًا، عند تنفيذ <code>cargo publish</code> لنشر الوحدة المصرفة في هذه المرحلة، ستتلقى تحذيرًا ثم خطأ:
</p>

<pre class="ipsCode">$ cargo publish
    Updating crates.io index
warning: manifest has no description, license, license-file, documentation, homepage or repository.
See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info.
--snip--
error: failed to publish to registry at https://crates.io

Caused by:
  the remote server responded with an error: missing or empty metadata fields: description, license. Please see https://doc.rust-lang.org/cargo/reference/manifest.html for how to upload metadata
</pre>

<p>
	تحدث هذه الأخطاء بسبب افتقاد بعض المعلومات المهمة؛ إذ أن الوصف والترخيص مطلوبان حتى يعرف الأشخاص ما تفعله الوحدة المصرفة الخاصة بك وتحت أي شروط يمكنهم استخدامها. أضف وصفًا في Cargo.toml بحيث يكون مجرد جملة أو جملتين، لأنه سيظهر مع الوحدة المصرفة الخاصة بك في نتائج البحث، أما بالنسبة لحقل <code>license</code>، فأنت بحاجة لمنح قيمة معرّف الترخيص.
</p>

<p>
	تُدرج مؤسسة لينكس لتبادل بيانات حزم البرمجيات Linux Foundation’s Software Package Data Exchange -أو اختصارًا SPDX- المعرّفات التي يمكنك استخدامها لهذه القيمة. على سبيل المثال، لتحديد أنك رخّصت وحدتك المصرفة باستخدام ترخيص MIT، أضف معرف <code>MIT</code>:
</p>

<p>
	اسم الملف: Cargo.toml
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2197_29" style=""><span class="pun">[</span><span class="pln">package</span><span class="pun">]</span><span class="pln">
name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"guessing_game"</span><span class="pln">
license </span><span class="pun">=</span><span class="pln"> </span><span class="str">"MIT"</span></pre>

<p>
	إذا أردت استخدام ترخيص غير موجود في SPDX، فأنت بحاجة إلى وضع نص هذا الترخيص في ملف، وتضمين الملف في مشروعك، ثم استخدام <code>license-file</code> لتحديد اسم هذا الملف بدلاً من ذلك من استخدام المفتاح <code>license</code>.
</p>

<p>
	التوجيه بشأن الترخيص المناسب لمشروعك هو خارج نطاق هذا الكتاب. يرخِّص الكثير من الأشخاص في مجتمع رست مشاريعهم بنفس طريقة رست ألا وهي باستخدام ترخيص مزدوج من "MIT OR Apache-2.0". تدلّك هذه الممارسة على أنه بإمكانك أيضًا تحديد معرّفات ترخيص متعددة مفصولة بـ <code>OR</code> لتضمين تراخيص متعددة لمشروعك.
</p>

<p>
	باستخدام الاسم المميز والإصدار والوصف والترخيص المضاف، أصبح ملف Cargo.toml الخاص بالمشروع جاهزًا للنشر على النحو التالي:
</p>

<p>
	اسم الملف: Cargo.toml
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2197_31" style=""><span class="pun">[</span><span class="pln">package</span><span class="pun">]</span><span class="pln">
name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"guessing_game"</span><span class="pln">
version </span><span class="pun">=</span><span class="pln"> </span><span class="str">"0.1.0"</span><span class="pln">
edition </span><span class="pun">=</span><span class="pln"> </span><span class="str">"2021"</span><span class="pln">
description </span><span class="pun">=</span><span class="pln"> </span><span class="str">"A fun game where you guess what number the computer has chosen."</span><span class="pln">
license </span><span class="pun">=</span><span class="pln"> </span><span class="str">"MIT OR Apache-2.0"</span><span class="pln">

</span><span class="pun">[</span><span class="pln">dependencies</span><span class="pun">]</span></pre>

<p>
	يصف توثيق كارجو البيانات الوصفية الأخرى التي يمكنك تحديدها للتأكد من أن الآخرين يمكنهم اكتشاف واستخدام وحدة التصريف الخاصة بك بسهولة أكبر.
</p>

<h2>
	النشر على Crates.io
</h2>

<p>
	الآن وبعد أن أنشأت حسابًا، وحفظت رمز <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>، واخترت اسمًا للوحدة المصرفة، وحددت البيانات الوصفية المطلوبة، فأنت جاهزٌ للنشر، إذ يؤدي نشر وحدة مصرفة إلى رفع إصدار معين إلى crates.io ليستخدمه الآخرون.
</p>

<p>
	كن حذرًا، لأن النشر دائم، ولا يمكن الكتابة فوق الإصدار مطلقًا، ولا يمكن حذف الشيفرة البرمجية. يتمثل أحد الأهداف الرئيسة لموقع crates.io بالعمل مثل أرشيف دائم للشيفرة البرمجية بحيث تستمر عمليات إنشاء جميع المشاريع التي تعتمد على الوحدات المصرفة من crates.io في العمل، والسماح بحذف نسخة ما سيجعل تحقيق هذا الهدف مستحيلًا، ومع ذلك، لا يوجد حد لعدد إصدارات الوحدات المصرفة التي يمكنك نشرها.
</p>

<p>
	نفّذ الأمر <code>cargo publish</code> مرةً أخرى. يجب أن تنجح الآن:
</p>

<pre class="ipsCode">$ cargo publish
    Updating crates.io index
   Packaging guessing_game v0.1.0 (file:///projects/guessing_game)
   Verifying guessing_game v0.1.0 (file:///projects/guessing_game)
   Compiling guessing_game v0.1.0
(file:///projects/guessing_game/target/package/guessing_game-0.1.0)
    Finished dev [unoptimized + debuginfo] target(s) in 0.19s
   Uploading guessing_game v0.1.0 (file:///projects/guessing_game)
</pre>

<p>
	تهانينا، فقد شاركت الآن الشيفرة الخاصة بك مع مجتمع رست، ويمكن لأي أحدٍ بسهولة إضافة الوحدة المصرفة الخاصة بك مثل اعتمادية لمشروعه.
</p>

<h2>
	نشر نسخة جديدة لوحدة مصرفة موجودة مسبقا
</h2>

<p>
	عندما تُجري تغييرات على الوحدة المصرفة الخاصة بك وتكون جاهزًا لطرح إصدار جديد، فإنك تغيّر قيمة <code>version</code> المحددة في ملف Cargo.toml الخاص بك وتعيد النشر. استخدم <a href="https://semver.org/" rel="external nofollow">قواعد الإدارة الدلالية لنُسخ البرمجيات Semantic Versioning rules</a> لتحديد رقم الإصدار التالي المناسب بناءً على التغييرات التي أجريتها، ومن ثم نفّذ <code>cargo publish</code> لرفع الإصدار الجديد.
</p>

<h2>
	تعطيل النسخ من Crates.io باستخدام cargo yank
</h2>

<p>
	على الرغم من أنه لا يمكنك إزالة الإصدارات السابقة للوحدة المصرفة، إلا أنه يمكنك منع أي مشاريع مستقبلية من إضافتها مثل اعتمادية جديدة، ويكون هذا مفيدًا عندما يُعطَّل إصدار الوحدة المصرفة لسبب أو لآخر، وفي مثل هذه الحالات، يدعم كارجو سحب yanking إصدار وحدة مصرفة.
</p>

<p>
	يمنع سحب إصدار ما المشاريع الجديدة من الاعتماد على هذا الإصدار مع السماح لجميع المشاريع الحالية التي تعتمد عليه بالاستمرار، إذ يعني السحب أن جميع المشاريع التي تحتوي على Cargo.lock لن تتعطّل، ولن تستخدم أي ملفات Cargo.lock المستقبلية المنشأة الإصدار المسحوب.
</p>

<p>
	لسحب نسخة من وحدة مصرفة، نفّذ <code>cargo yank</code> في دليل الوحدة المصرفة الذي نشرتَه سابقًا، وحدّد أي إصدار تريد إزالته. على سبيل المثال، إذا نشرنا وحدة مصرفة باسم <code>guessing_game</code> الإصدار 1.0.1 وأردنا انتزاعه، في مجلد المشروع <code>guessing_game</code> ننفّذ ما يلي:
</p>

<pre class="ipsCode">$ cargo yank --vers 1.0.1
    Updating crates.io index
        Yank guessing_game@1.0.1
</pre>

<p>
	يمكنك أيضًا التراجع عن عملية السحب من خلال إضافة <code>undo--</code> إلى الأمر والسماح للمشاريع بالاعتماد على الإصدار مرة أخرى:
</p>

<pre class="ipsCode">$ cargo yank --vers 1.0.1 --undo
    Updating crates.io index
      Unyank guessing_game_:1.0.1
</pre>

<p>
	لا تحذف عملية السحب أي شيفرة برمجية، إذ من غير الممكن على سبيل المثال حذف بيانات حساسة رُفعَت بالخطأ. إذا حدث ذلك، يجب عليك إعادة تعيين تلك البيانات على الفور.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch14-00-more-about-cargo.html" rel="external nofollow">More About Cargo and Crates.io</a> من كتاب <a href="https://doc.rust-lang.org/stable/book/title-page.html/" rel="external nofollow">The Rust Programming Language</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/rust/%D9%85%D8%B3%D8%A7%D8%AD%D8%A9-%D8%B9%D9%85%D9%84-%D9%83%D8%A7%D8%B1%D8%AC%D9%88-cargo-workspaces-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D9%88%D8%AA%D8%AD%D9%85%D9%8A%D9%84-%D8%AD%D8%B2%D9%85%D8%A9-%D9%85%D9%86-cratesio-r2014/" rel="">مساحة عمل كارجو Cargo Workspaces في لغة رست وتحميل حزمة من crates.io</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D9%8A%D8%A7%D8%B1-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%AD%D9%84%D9%82%D8%A7%D8%AA-loops-%D9%88%D8%A7%D9%84%D9%85%D9%83%D8%B1%D8%B1%D8%A7%D8%AA-iterators-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1995/" rel="">الاختيار بين الحلقات Loops والمكررات Iterators في لغة رست</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-command-line-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AF%D8%AE%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%B1%D8%AC-r1969/" rel="">كتابة برنامج سطر أوامر Command Line بلغة رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%B3%D8%A7%D8%B1%D8%A7%D8%AA-paths-%D9%88%D8%A7%D9%84%D9%86%D8%B7%D8%A7%D9%82-%D8%A7%D9%84%D8%AE%D8%A7%D8%B5-%D8%A8%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1874/" rel="">المسارات paths والنطاق الخاص بها في لغة رست Rust</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2013</guid><pubDate>Sat, 24 Jun 2023 13:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x627;&#x62E;&#x62A;&#x64A;&#x627;&#x631; &#x628;&#x64A;&#x646; &#x627;&#x644;&#x62D;&#x644;&#x642;&#x627;&#x62A; Loops &#x648;&#x627;&#x644;&#x645;&#x643;&#x631;&#x631;&#x627;&#x62A; Iterators &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A;</title><link>https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D9%8A%D8%A7%D8%B1-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%AD%D9%84%D9%82%D8%A7%D8%AA-loops-%D9%88%D8%A7%D9%84%D9%85%D9%83%D8%B1%D8%B1%D8%A7%D8%AA-iterators-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1995/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_06/----Loops--Iterators----Rust.png.80ccf0fb20caa9375df63adec474374a.png" /></p>
<p>
	تعرفنا في الفصل السابق "<a href="https://academy.hsoub.com/programming/rust/%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%B3%D9%84%D8%B3%D9%84%D8%A9-%D9%85%D9%86-%D8%A7%D9%84%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D9%83%D8%B1%D8%B1%D8%A7%D8%AA-iterators-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1993/" rel="">معالجة سلسلة من العناصر باستخدام المكررات iterators</a>" والذي يليه "<a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D9%83%D8%B1%D8%B1%D8%A7%D8%AA-iterators-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1994/" rel="">استخدام المكررات Iterators في تطبيق سطر أوامر</a>" على المكررات Iterators وكيفية استخدامها عمليًا ولعل السؤال الذي خطر ببالك الآن بعد قراءتهما هو: أيّ طرق التطبيق ينبغي عليك استعمالها عند برمجتك لبرنامجك ولماذا؟ التطبيق الأصلي في الشيفرة 21، أم الإصدار باستخدام المكررات في الشيفرة 22 مقال "استخدام المكررات Iterators في تطبيق سطر أوامر بلغة رست"؟ يفضِّل معظم مبرمجي لغة رست استخدام المكررات، وعلى الرغم من أن هذه الطريقة أصعب فهمًا في البداية إلا أنك ستفضلها بعد الاعتياد عليها وعلى محولات المكررات المختلفة، إذ ستركّز عندها شيفرتك البرمجية على الهدف العام للحلقة بدلًا من إضاعة الوقت في ضبط الأجزاء المختلفة من الحلقات وإنشاء أشعة جديدة. تخلّصنا هذه الطريقة من الكثير من الشيفرات البرمجية الاعتيادية بحيث يكون من الأسهل رؤية المقصد الأساسي من الشيفرة البرمجية على نحوٍ فريد لكل استخدام، مثل ترشيح كل عنصر في المكرر بحسب شرط ما.
</p>

<p>
	لكن هل الطريقتان متساويتان حقًا؟ يقول الافتراض المنطقي: الحلقة التي يكون تطبيقها على مستوى منخفض أسرع. دعنا نتناقش بالتفاصيل.
</p>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة تطوير التطبيقات باستخدام لغة Python
		</p>

		<p class="banner-subtitle">
			احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/python-application-development" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<img alt="دورة تطوير التطبيقات باستخدام لغة Python" src="https://academy.hsoub.com/learn/assets/images/courses/python-application-development.png">
	</div>
</div>

<h2>
	المقارنة بين أداء الحلقات والمكررات
</h2>

<p>
	عليك معرفة أيّ التطبيقين أسرع، الحلقات أم المكررات؟ وذلك لتحديد متى يجب عليك استخدام أحد التطبيقين؛ هل إصدار الدالة <code>search</code> (من المقال السابق) باستخدام حلقة <code>for</code> أسرع أم إصدار المكررات؟
</p>

<p>
	ننفّذ اختبار أداء بكتابة كامل محتويات رواية "مغامرات شيرلوك هولمز The Adventures of Sherlock Holmes" لكاتبها سير آرثر كونان دويل Sir Arthur Conan Doyle إلى <code>String</code> والبحث عن الكلمة "the" في المحتويات. إليك نتائج اختبار الأداء على كلا الإصدارين لدالة <code>search</code> باستخدام حلقة <code>for</code>، وباستخدام المكرّرات:
</p>

<pre class="ipsCode">test bench_search_for  ... bench:  19,620,300 ns/iter (+/- 915,700)
test bench_search_iter ... bench:  19,234,900 ns/iter (+/- 657,200)
</pre>

<p>
	كان إصدار المكررات أسرع قليلًا. لن نشرح الشيفرة البرمجية الخاصة باختبار الأداء هنا، لأن الهدف من المقال ليس برهنة أن الإصدارين متساويين بالأداء بل هو لملاحظة أداء كل طريقة بالنسبة للأخرى.
</p>

<p>
	ينبغي عليك التحقق من نصوص متفاوتة الطول للوسيط <code>contents</code> للحصول على اختبار أداء أكثر وضوًحا، واستخدام كلمات مختلفة من أطوال متفاوتة للوسيط <code>query</code> وتجربة مختلف أنواع الحالات. ما نريد الوصول إليه هنا هو التالي: على الرغم من أن المكررات تستخدم تطبيقًا مجرّدًا عالي المستوى، إلا أنها تُصرَّف إلى شيفرة برمجية مماثلة لشيفرة تطبيق منخفض المستوى كتبتها بنفسك، إذ أن المكررات هي من الطرق المجرّدة عديمة الحمل zero-cost abstraction في رست، وهذا يعني أن استخدام التجريد لن يؤثر على وقت التشغيل. هذا الأمر مماثل لكيفية تعريف بيارن ستروستروب Bjarne Stroustrup مصمّم <a href="https://academy.hsoub.com/programming/cpp/" rel="">لغة سي بلس بلس C++‎</a> ومطبّقها لمبدأ انعدام الحمل غير المباشر zero-overhead في كتابه "أساسيات سي بلس بلس Foundations of C++‎"‏ (2012):
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p data-gramm="false">
		تتبع تطبيقات لغة سي بلس بلس عمومًا مبدأ انعدام الحمل غير المباشر: إذ أنك لا تدفع عمّا لا تستخدمه، إضافةً إلى ذلك: لا يمكن للطريقة التي تتبعها التعامل مع الشيفرة البرمجية بطريقة أفضل من ذلك.
	</p>
</blockquote>

<p>
	الشيفرة البرمجية التالية هي مثال آخر مأخوذ من برنامج فك ترميز صوت، إذ تستخدم خوارزمية فك الترميز عملية التوقع الخطي الرياضي لتوقّع القيم المستقبلية بناءً على دالة خطية تأخذ العينات السابقة. تستخدم هذه الشيفرة البرمجية سلسلة مكررات لإنجاز بعض العمليات الرياضية على ثلاثة متغيرات موجودة في النطاق scope، هي: <code>buffer</code> شريحة البيانات، ومصفوفة من 12 عنصر <code>coefficients</code> وأي مقدار إزاحة بالبيانات مُخزَّن في <code>qlb_shift</code>، وقد صرّحنا عن هذه المتغيرات داخل المثال دون إسناد أي قيمة لها. على الرغم من أن هذه الشيفرة البرمجية ليست مفيدة خارج سياقها إلا أنها مثال مختصر وواقعي على كيفية ترجمة رست للأفكار والتطبيقات عالية المستوى إلى مستوى منخفض.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3927_9" style=""><span class="pln">let buffer</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">mut </span><span class="pun">[</span><span class="pln">i32</span><span class="pun">];</span><span class="pln">
let coefficients</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">i64</span><span class="pun">;</span><span class="pln"> </span><span class="lit">12</span><span class="pun">];</span><span class="pln">
let qlp_shift</span><span class="pun">:</span><span class="pln"> i16</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">for</span><span class="pln"> i in </span><span class="lit">12.</span><span class="pun">.</span><span class="pln">buffer</span><span class="pun">.</span><span class="pln">len</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let prediction </span><span class="pun">=</span><span class="pln"> coefficients</span><span class="pun">.</span><span class="pln">iter</span><span class="pun">()</span><span class="pln">
                                 </span><span class="pun">.</span><span class="pln">zip</span><span class="pun">(&amp;</span><span class="pln">buffer</span><span class="pun">[</span><span class="pln">i </span><span class="pun">-</span><span class="pln"> </span><span class="lit">12.</span><span class="pun">.</span><span class="pln">i</span><span class="pun">])</span><span class="pln">
                                 </span><span class="pun">.</span><span class="typ">map</span><span class="pun">(|(&amp;</span><span class="pln">c</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">s</span><span class="pun">)|</span><span class="pln"> c </span><span class="pun">*</span><span class="pln"> s as i64</span><span class="pun">)</span><span class="pln">
                                 </span><span class="pun">.</span><span class="pln">sum</span><span class="pun">::&lt;</span><span class="pln">i64</span><span class="pun">&gt;()</span><span class="pln"> </span><span class="pun">&gt;&gt;</span><span class="pln"> qlp_shift</span><span class="pun">;</span><span class="pln">
    let delta </span><span class="pun">=</span><span class="pln"> buffer</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
    buffer</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> prediction as i32 </span><span class="pun">+</span><span class="pln"> delta</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تمرّ الشيفرة البرمجية على القيم الاثنا عشر الموجودة في <code>coefficients</code> وتستخدم التابع <code>zip</code> لاقتران القيم الاثنا عشر الحالية مع القيم الاثني العشر السابقة الموجودة في <code>buffer</code> وذلك لحساب قيمة التوقع <code>prediction</code>، ثم نضرب قيمة كل زوج على حدى ونجمع النتيجة ونُزيح shift البتات bits في نتيجة الجمع بمقدار <code>qlb_shift</code> بت إلى اليمين.
</p>

<p>
	تركّز العمليات الحسابية في تطبيقات مثل فك ترميز الصوت على الأداء في المقام الأول. نُنشئ هنا مكررًا باستخدام محوّلَين adaptor ومن ثم نستهلك القيمة. ما هي شيفرة أسيمبلي Assembly التي ستُصرَّف إليها شيفرة رست هذه؟ تُصرَّف حتى الوقت الحالي إلى نفس شيفرة أسيمبلي التي قد تكتبها بنفسك. ليس هناك أي حلقة للمكرر الخاص بالقيم <code>coefficients</code>، إذ تعرف رست أن هناك 12 تكرارًا فقط، لذا فهي تنشر الحلقة؛ وعملية النشر unrolling هي عملية لتحسين الأداء، إذ يُزال فيها الحمل الإضافي غير المباشر الخاص بالشيفرة البرمجية المُتحكمة بالحلقة وتُولَّد شيفرة برمجية متكررة بدلًا من ذلك للشيفرة البرمجية في كل تكرار من الحلقة.
</p>

<p>
	تُخزَّن جميع المعاملات في المسجلات registers، مما يعني أن الوصول إلى القيم عملية سريعة، ولا يوجد هناك أي تحقق للقيود على مصفوفة الوصول access array خلال وقت التشغيل. ترفع جميع هذه الخطوات التي تفعلها رست لتحسين الأداء من فعالية أداء الشيفرة البرمجية الناتجة كثيرًا. الآن وبعد معرفتك لهذا، يمكنك استخدام المكررات والمغلفات دون أي خوف، إذ يجعلان من شيفرتك البرمجية ذات مستوى تطبيقي أعلى دون التفريط بسرعة الأداء عند وقت التشغيل.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch13-00-functional-features.html" rel="external nofollow">Functional Language Features: Iterators and Closures</a> من كتاب <a href="https://doc.rust-lang.org/stable/book/title-page.html/" rel="external nofollow">The Rust Programming Language</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D8%AE%D8%B5%D9%8A%D8%B5-%D9%86%D8%B3%D8%AE-%D9%85%D8%B4%D8%B1%D9%88%D8%B9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D9%88%D9%86%D8%B4%D8%B1%D9%87%D8%A7-%D8%B9%D9%84%D9%89-cratesio-r2013/" rel="">تخصيص نسخ مشروع بلغة رست ونشرها على crates.io</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D9%83%D8%B1%D8%B1%D8%A7%D8%AA-iterators-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1994/" rel="">استخدام المكررات Iterators في تطبيق سطر أوامر بلغة رست</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A8%D9%86%D9%8A%D8%A9-match-%D9%84%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A8%D8%B3%D9%8A%D8%B1-%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1852/" rel="">بنية match للتحكم بسير برامج لغة رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A8%D8%B3%D9%8A%D8%B1-%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%B1%D8%A7%D8%B3%D8%AA-rust-r1785/" rel="">التحكم بسير تنفيذ برامج راست Rust</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1995</guid><pubDate>Sat, 17 Jun 2023 13:03:00 +0000</pubDate></item><item><title>&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x627;&#x644;&#x645;&#x643;&#x631;&#x631;&#x627;&#x62A; Iterators &#x641;&#x64A; &#x62A;&#x637;&#x628;&#x64A;&#x642; &#x633;&#x637;&#x631; &#x623;&#x648;&#x627;&#x645;&#x631; &#x628;&#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A;</title><link>https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D9%83%D8%B1%D8%B1%D8%A7%D8%AA-iterators-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1994/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_06/-------Rust---Iterators.png.57a9be65e08c83c145dd56f779b0d80c.png" /></p>
<p>
	يمكننا الآن تحسين مشروع سطر الأوامر الذي نفذناه في فصل سابق بعنوان <a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-command-line-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AF%D8%AE%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%B1%D8%AC-r1969/" rel="">كتابة برنامج سطر أوامر Command Line بلغة رست Rust</a> بعد تعرُّفنا على المكرّرات في المقال السابق <a href="https://academy.hsoub.com/programming/rust/%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%B3%D9%84%D8%B3%D9%84%D8%A9-%D9%85%D9%86-%D8%A7%D9%84%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D9%83%D8%B1%D8%B1%D8%A7%D8%AA-iterators-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1993/" rel="">معالجة سلسلة من العناصر باستخدام المكررات iterators</a>، إذ سنستخدمها لجعل شيفرتنا البرمجية أكثر وضوحًا واختصارًا. دعنا نلقي نظرةً على طريقة تطبيق المكررات في مشروعنا والتي ستجعل منه إصدارًا محسنًا خاصةً على الدالتين <code>Config::build</code> و <code>search</code>.
</p>

<h2>
	إزالة clone باستخدام المكرر
</h2>

<p>
	أضفنا في الشيفرة 6 (من فصل <a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A5%D8%B9%D8%A7%D8%AF%D8%A9-%D8%A8%D9%86%D8%A7%D8%A1-%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%84%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-modularity-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-r1974/" rel="">كتابة برنامج سطر أوامر بلغة رست: إعادة بناء التعليمات البرمجية لتحسين النمطية Modularity والتعامل مع الأخطاء</a>) شيفرةً برمجية تأخذ شريحةً slice من القيم ذات النوع <code>String</code> وأنشأنا بها نسخةً instance من الهيكل <code>Config</code> بالمرور على الشريحة ونسخ القيم والسماح بالهيكل <code>Config</code> بامتلاك هذه القيم. سنُعيد كتابة التطبيق ذاته الخاص بالدالة <code>Config::build</code> في الشيفرة 17 كما كانت في الشيفرة 23 (الفصل 12):
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_664_8" style=""><span class="pln">impl </span><span class="typ">Config</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pub fn build</span><span class="pun">(</span><span class="pln">args</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;[</span><span class="typ">String</span><span class="pun">])</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Result</span><span class="pun">&lt;</span><span class="typ">Config</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="str">'</span><span class="kwd">static</span><span class="pln"> str</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">len</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">3</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Err</span><span class="pun">(</span><span class="str">"not enough arguments"</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        let query </span><span class="pun">=</span><span class="pln"> args</span><span class="pun">[</span><span class="lit">1</span><span class="pun">].</span><span class="pln">clone</span><span class="pun">();</span><span class="pln">
        let file_path </span><span class="pun">=</span><span class="pln"> args</span><span class="pun">[</span><span class="lit">2</span><span class="pun">].</span><span class="pln">clone</span><span class="pun">();</span><span class="pln">

        let ignore_case </span><span class="pun">=</span><span class="pln"> env</span><span class="pun">::</span><span class="pln">var</span><span class="pun">(</span><span class="str">"IGNORE_CASE"</span><span class="pun">).</span><span class="pln">is_ok</span><span class="pun">();</span><span class="pln">

        </span><span class="typ">Ok</span><span class="pun">(</span><span class="typ">Config</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            query</span><span class="pun">,</span><span class="pln">
            file_path</span><span class="pun">,</span><span class="pln">
            ignore_case</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">})</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	[الشيفرة 17: إعادة بناء الدالة <code>Config::build</code> من الشيفرة 23 (الفصل 12)]
</p>

<p>
	ذكرنا وقتها أنه ليس علينا القلق بخصوص استدعاءات <code>clone</code> غير الفعالة لأننا سنزيلها في المستقبل. حسنًا، أتى الوقت الآن.
</p>

<p>
	نحتاج <code>clone</code> هنا لوجود شريحة بعناصر <code>String</code> في المعامل <code>args</code>، إلا أن الدالة <code>build</code> لا تمتلك <code>args</code>، ولإعادة ملكية نسخة <code>Config</code>، اضطررنا لنسخ القيم من الحقول <code>query</code> و <code>file_path</code> الموجودة في الهيكل <code>Config</code> بحيث تمتلك نسخة <code>Config</code> القيم الموجودة في حقوله.
</p>

<p>
	أصبح بإمكاننا -بمعرفتنا الجديدة بخصوص المكررات- التعديل على الدالة <code>build</code> لأخذ ملكية مكرر مثل وسيط لها بدلًا من استعارة شريحة، وسنستخدم وظائف المكرر بدلًا من الشيفرة البرمجية التي تتحقق من طول السلسلة النصية وتمرّ على عناصرها في مواقع محددة، وسيوضِّح ذلك استخدام <code>Config::build</code> لأن المكرر يستطيع الحصول على هذه القيم.
</p>

<p>
	بعد أخذ الدالة <code>Config::build</code> لملكية المكرر وتوقف استخدامها لعمليات الفهرسة indexing التي تستعير القيم، أصبح بإمكاننا نقل قيم <code>String</code> من المكرر إلى الهيكل <code>Config</code> بدلًا من استدعاء <code>clone</code> وإنشاء تخصيص allocation جديد.
</p>

<h3>
	إزالة المكرر المعاد مباشرة
</h3>

<p>
	افتح ملف src/main.rs الخاص بمشروع الدخل والخرج، والذي يجب أن يبدو مماثلًا لما يلي:
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_664_10" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let args</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> env</span><span class="pun">::</span><span class="pln">args</span><span class="pun">().</span><span class="pln">collect</span><span class="pun">();</span><span class="pln">

    let config </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Config</span><span class="pun">::</span><span class="pln">build</span><span class="pun">(&amp;</span><span class="pln">args</span><span class="pun">).</span><span class="pln">unwrap_or_else</span><span class="pun">(|</span><span class="pln">err</span><span class="pun">|</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        eprintln</span><span class="pun">!(</span><span class="str">"Problem parsing arguments: {err}"</span><span class="pun">);</span><span class="pln">
        process</span><span class="pun">::</span><span class="pln">exit</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    </span><span class="com">// --snip--</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنعدّل أولًا بداية الدالة <code>main</code> التي كانت موجودة في <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D9%8A%D8%A6%D8%A9-%D9%88%D8%B7%D8%A8%D8%A7%D8%B9%D8%A9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1979/" rel="">الشيفرة 24</a> من فصل سابق لتكون الشيفرة 18 التي تستخدم مكررًا، إلا أنها لن تُصرَّف بنجاح إلا بعد تعديلنا للدالة <code>Config::build</code> أيضًا.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_664_12" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let config </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Config</span><span class="pun">::</span><span class="pln">build</span><span class="pun">(</span><span class="pln">env</span><span class="pun">::</span><span class="pln">args</span><span class="pun">()).</span><span class="pln">unwrap_or_else</span><span class="pun">(|</span><span class="pln">err</span><span class="pun">|</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        eprintln</span><span class="pun">!(</span><span class="str">"Problem parsing arguments: {err}"</span><span class="pun">);</span><span class="pln">
        process</span><span class="pun">::</span><span class="pln">exit</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    </span><span class="com">// --snip--</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	[الشيفرة 18: تمرير القيمة المعادة من <code>env::args</code> إلى <code>Config::build</code>]
</p>

<p>
	تُعيد الدالة <code>env::args</code> مكررًا، إذ يمكننا تمرير ملكية المكرّر المُعاد من <code>env::args</code> إلى <code>Config::build</code> الآن مباشرةً بدلًا من تجميع قيم المكرر في شعاع ثم تمرير شريحة إلى الدالة <code>Config::build</code>.
</p>

<p>
	نحتاج إلى تحديث تعريف الدالة <code>Config::build</code> في ملف src/lib.rs الخاص بمشروعنا. دعنا نغير بصمة الدالة <code>Config::build</code> لتبدو على نحوٍ مماثل للشيفرة 19، إلا أن هذا لن يُصرَّف بنجاح لأننا بحاجة لتحديث متن الدالة أيضًا.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_664_14" style=""><span class="pln">impl </span><span class="typ">Config</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pub fn build</span><span class="pun">(</span><span class="pln">
        mut args</span><span class="pun">:</span><span class="pln"> impl </span><span class="typ">Iterator</span><span class="pun">&lt;</span><span class="typ">Item</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">&gt;,</span><span class="pln">
    </span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Result</span><span class="pun">&lt;</span><span class="typ">Config</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="str">'</span><span class="kwd">static</span><span class="pln"> str</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// --snip--</span></pre>

<p style="text-align: center;">
	[الشيفرة 19: تحديث بصمة الدالة <code>Config::build</code> بحيث تتوقع تمرير مكرر]
</p>

<p>
	يوضّح توثيق المكتبة القياسية بالنسبة للدالة <code>env::args</code> بأن نوع المكرر المُعاد هو <code>std::env::Args</code> وأن هذا النوع يطبّق السمة <code>Iterator</code> ويُعيد قيمًا من النوع <code>String</code>.
</p>

<p>
	حدّثنا بصمة الدالة <code>Config::build</code> بحيث يحتوي المعامل <code>args</code> على نوع معمم generic type بحدّ السمة trait bound <code>impl Iterator&lt;Item = String&gt;‎</code>‎ بدلًا من <code>‎&amp;[String]‎</code>. ناقشنا الصيغة <code>impl Trait</code> <a href="https://academy.hsoub.com/programming/rust/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D9%85%D8%A9-generic-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1935/" rel="">سابقًا</a> وهي تعني أن <code>args</code> يمكن أن يكون أي نوع يطبّق النوع <code>Iterator</code> ويُعيد عناصرًا من النوع <code>String</code>.
</p>

<p>
	يمكننا إضافة الكلمة المفتاحية <code>mut</code> إلى توصيف المعامل <code>args</code> لجعله متغيّرًا mutable بالنظر إلى أننا نأخذ ملكية <code>args</code> وسنعدّل <code>args</code> بالمرور ضمنه.
</p>

<h3>
	استخدام توابع السمة Iterator بدلا من الفهرسة
</h3>

<p>
	سنصحح متن الدالة <code>Config::build</code>، فنحن نعلم أنه بإمكاننا استدعاء التابع <code>next</code> على <code>args</code> لأنها تطبّق السمة <code>Iterator</code>. نحدّث في الشيفرة 20 الشيفرة البرمجية التي كانت موجودة في <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D9%8A%D8%A6%D8%A9-%D9%88%D8%B7%D8%A8%D8%A7%D8%B9%D8%A9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1979/" rel="">الشيفرة 23</a> بحيث نستخدم التابع <code>next</code>:
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_664_16" style=""><span class="pln">impl </span><span class="typ">Config</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pub fn build</span><span class="pun">(</span><span class="pln">
        mut args</span><span class="pun">:</span><span class="pln"> impl </span><span class="typ">Iterator</span><span class="pun">&lt;</span><span class="typ">Item</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">&gt;,</span><span class="pln">
    </span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Result</span><span class="pun">&lt;</span><span class="typ">Config</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="str">'</span><span class="kwd">static</span><span class="pln"> str</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        args</span><span class="pun">.</span><span class="pln">next</span><span class="pun">();</span><span class="pln">

        let query </span><span class="pun">=</span><span class="pln"> match args</span><span class="pun">.</span><span class="pln">next</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">arg</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> arg</span><span class="pun">,</span><span class="pln">
            </span><span class="typ">None</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Err</span><span class="pun">(</span><span class="str">"Didn't get a query string"</span><span class="pun">),</span><span class="pln">
        </span><span class="pun">};</span><span class="pln">

        let file_path </span><span class="pun">=</span><span class="pln"> match args</span><span class="pun">.</span><span class="pln">next</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">arg</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> arg</span><span class="pun">,</span><span class="pln">
            </span><span class="typ">None</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Err</span><span class="pun">(</span><span class="str">"Didn't get a file path"</span><span class="pun">),</span><span class="pln">
        </span><span class="pun">};</span><span class="pln">

        let ignore_case </span><span class="pun">=</span><span class="pln"> env</span><span class="pun">::</span><span class="pln">var</span><span class="pun">(</span><span class="str">"IGNORE_CASE"</span><span class="pun">).</span><span class="pln">is_ok</span><span class="pun">();</span><span class="pln">

        </span><span class="typ">Ok</span><span class="pun">(</span><span class="typ">Config</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            query</span><span class="pun">,</span><span class="pln">
            file_path</span><span class="pun">,</span><span class="pln">
            ignore_case</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">})</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 20: تعديل محتوى الدالة <code>Config::build</code> بحيث نستخدم توابع المكرر]
</p>

<p>
	تذكّر أن أول قيمة من القيم المُعادة من الدالة <code>env::args</code> هي اسم البرنامج، لذا نريد تجاهلها والحصول على القيمة التي تليها، ولذلك نستدعي <code>next</code> أولًا دون فعل أي شيء بالقيمة المُعادة، ثم نستدعي <code>next</code> مرةً أخرى للحصول على القيمة التي نريد وضعها في حقل <code>query</code> من الهيكل <code>Config</code>. إذا أعادت <code>next</code> القيمة <code>Some</code>، سنستخدم <code>match</code> لاستخلاص القيمة، أما إذا أعادت <code>None</code>، فهذا يعني عدم وجود وسطاء كافية من المستخدم وعندها نُعيد من الدالة القيمة <code>Err</code> مبكرًا، ونفعل ذلك مجددًا للتعامل مع القيمة <code>file_path</code>.
</p>

<h2>
	جعل الشيفرة البرمجية أكثر وضوحا باستخدام محولات المكرر
</h2>

<p>
	يمكننا أيضًا استغلال ميزة من مزايا المكررات في الدالة <code>search</code> ضمن مشروعنا، وهي موضّحة في <a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-r1978/" rel="">الشيفرة 21</a> و<a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D9%8A%D8%A6%D8%A9-%D9%88%D8%B7%D8%A8%D8%A7%D8%B9%D8%A9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1979/" rel="">الشيفرة 19</a> (من فصل سابق):
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_664_18" style=""><span class="pln">pub fn search</span><span class="pun">&lt;</span><span class="str">'a&gt;(query: &amp;str, contents: &amp;'</span><span class="pln">a str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;&amp;</span><span class="str">'</span><span class="pln">a str</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let mut results </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">();</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> line in contents</span><span class="pun">.</span><span class="pln">lines</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> line</span><span class="pun">.</span><span class="pln">contains</span><span class="pun">(</span><span class="pln">query</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            results</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">line</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    results
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 21: تطبيق الدالة <code>search</code> من الشيفرة 19 (الفصل 12)]
</p>

<p>
	يمكننا كتابة هذه الشيفرة البرمجية بطريقة مختصرة باستخدام توابع محولات المكرر، إذ يسمح لنا ذلك أيضًا بوجود شعاع وسيط متغيّر نسميه <code>results</code>. يفضِّل أسلوب لغات البرمجة العمليّة تقليل كمية الحالات المتغيّرة لجعل الشيفرة البرمجية أكثر وضوحًا، إذ قد تفتح لنا إزالة الحالة المتغيّرة فرصةً لجعل عمليات البحث تُنفَّذ بصورةٍ متزامنة، لأنه لن يكون علينا حينها إدارة الوصول المتتالي إلى الشعاع <code>results</code>. توضح الشيفرة 22 هذا التغيير:
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_664_20" style=""><span class="pln">pub fn search</span><span class="pun">&lt;</span><span class="str">'a&gt;(query: &amp;str, contents: &amp;'</span><span class="pln">a str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;&amp;</span><span class="str">'</span><span class="pln">a str</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    contents
        </span><span class="pun">.</span><span class="pln">lines</span><span class="pun">()</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">filter</span><span class="pun">(|</span><span class="pln">line</span><span class="pun">|</span><span class="pln"> line</span><span class="pun">.</span><span class="pln">contains</span><span class="pun">(</span><span class="pln">query</span><span class="pun">))</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">collect</span><span class="pun">()</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 22: استخدام توابع محول المكرر في تطبيق الدالة <code>search</code>]
</p>

<p>
	تذكر أن الهدف من الدالة <code>search</code> هو إعادة جميع الأسطر الموجودة في <code>contents</code> التي تحتوي على <code>query</code>. تستخدم هذه الشيفرة البرمجية -بصورةٍ مشابهة لمثال <code>filter</code> في الشيفرة 16- محوّل <code>filter</code> للمحافظة على السطور التي يُعيد فيها التعبير <code>line.contains(query)‎</code> القيمة <code>true</code>. يمكننا تجميع الأسطر الناتجة في شعاع آخر باستخدام <code>collect</code>.
</p>

<p>
	هذه الطريقة أبسط بكثير. جرّب تنفيذ التعديلات ذاتها بحيث تستخدم توابع المكرر في الدالة <code>search_case_insensitive</code> بصورةٍ مشابهة.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch13-00-functional-features.html" rel="external nofollow">Functional Language Features: Iterators and Closures</a> من كتاب <a href="https://doc.rust-lang.org/stable/book/title-page.html/" rel="external nofollow">The Rust Programming Language</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D9%8A%D8%A7%D8%B1-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%AD%D9%84%D9%82%D8%A7%D8%AA-loops-%D9%88%D8%A7%D9%84%D9%85%D9%83%D8%B1%D8%B1%D8%A7%D8%AA-iterators-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1995/" rel="">الاختيار بين الحلقات Loops والمكررات Iterators في لغة رست</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%B3%D9%84%D8%B3%D9%84%D8%A9-%D9%85%D9%86-%D8%A7%D9%84%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D9%83%D8%B1%D8%B1%D8%A7%D8%AA-iterators-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1993/" rel="">معالجة سلسلة من العناصر باستخدام المكررات iterators في لغة رست</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%84%D8%B9%D8%A8%D8%A9-%D8%AA%D8%AE%D9%85%D9%8A%D9%86-%D8%A7%D9%84%D8%A3%D8%B1%D9%82%D8%A7%D9%85-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1765/" rel="">برمجة لعبة تخمين الأرقام بلغة رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1959/" rel="">كتابة الاختبارات في لغة رست Rust</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1994</guid><pubDate>Sat, 10 Jun 2023 13:09:00 +0000</pubDate></item><item><title>&#x645;&#x639;&#x627;&#x644;&#x62C;&#x629; &#x633;&#x644;&#x633;&#x644;&#x629; &#x645;&#x646; &#x627;&#x644;&#x639;&#x646;&#x627;&#x635;&#x631; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x627;&#x644;&#x645;&#x643;&#x631;&#x631;&#x627;&#x62A; iterators &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A;</title><link>https://academy.hsoub.com/programming/rust/%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%B3%D9%84%D8%B3%D9%84%D8%A9-%D9%85%D9%86-%D8%A7%D9%84%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D9%83%D8%B1%D8%B1%D8%A7%D8%AA-iterators-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1993/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_06/------iterators---rust.png.faaa23a2ebbf38d2c056a3296f5e4228.png" /></p>
<p>
	يسمح لك <a href="https://wiki.hsoub.com/Design_Patterns/iterator" rel="external">نمط المكرّر iterator pattern</a> بإنجاز مهمة ما على سلسلة من العناصر بصورةٍ متتالية، والمكرّر مسؤول عن المنطق الخاص بالمرور على كل عنصر وتحديد مكان انتهاء السلسلة، إذ ليس من الواجب عليك إعادة تطبيق هذا المنطق بنفسك عند استخدام المكررات.
</p>

<p>
	المكررات في رست كسولة، بمعنى أنها لا تمتلك أي تأثير حتى تستدعي أنت التابع الذي يستخدم المكرر، فعلى سبيل المثال نُنشئ الشيفرة 10 مكرّرًا على العناصر الموجودة في الشعاع ‏ <code>v1</code> باستدعاء التابع <code>iter</code> المعرّف على <code>Vec&lt;T&gt;‎</code>، ولا تفعل تلك الشيفرة البرمجية أي شيء مفيد.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9813_8" style=""><span class="pln">    let v1 </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">];</span><span class="pln">

    let v1_iter </span><span class="pun">=</span><span class="pln"> v1</span><span class="pun">.</span><span class="pln">iter</span><span class="pun">();</span></pre>

<p>
	[الشيفرة 10: إنشاء مكرر]
</p>

<p>
	نخزّن المكرر في المتغير <code>v1_iter</code>، ويمكننا استخدامه بطرق عدّة بعد إنشائه. على سبيل المثال، استخدمنا حلقة <code>for</code> في <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A8%D8%B3%D9%8A%D8%B1-%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%B1%D8%A7%D8%B3%D8%AA-rust-r1785/" rel="">الشيفرة 5</a> من فصل سابق للمرور على مصفوفة وذلك لتنفيذ شيفرة برمجية على كل من عناصرها، وفي الحقيقة كان المكرّر موجودًا ضمنيًا في تلك الشيفرة لتحقيق ذلك إلا أننا لم نتكلم عن ذلك سابقًا.
</p>

<p>
	نفصل في الشيفرة 11 إنشاء المكرر من استخدامه في الحلقة <code>for</code>، فعندما تُستدعى الحلقة <code>for</code> باستخدام المكرر <code>v1_iter</code>، يُستخدم كل عنصر في المكرر مرةً تلو الأخرى في كل دورة للحلقة، وهذا يطبع كل قيمة من قيم العناصر.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9813_10" style=""><span class="pln">    let v1 </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">];</span><span class="pln">

    let v1_iter </span><span class="pun">=</span><span class="pln"> v1</span><span class="pun">.</span><span class="pln">iter</span><span class="pun">();</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> val in v1_iter </span><span class="pun">{</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"Got: {}"</span><span class="pun">,</span><span class="pln"> val</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	[الشيفرة 11: استخدام مكرر في حلقة <code>for</code>]
</p>

<p>
	يمكنك كتابة شيفرة برمجية تفعل الأمر نفسه في لغات البرمجة التي لا تستخدم المكررات في مكتبتها القياسية وذلك عن طريق البدء بالمتغير من الدليل‏ 0 واستخدام قيمة المتغير يمثابة دليلٍ للشعاع للحصول على قيمة ومن ثم زيادة قيمة المتغير في الحلقة حتى تصل قيمته لعدد العناصر الكلية في الشعاع.
</p>

<p>
	تتعامل المكررات مع المنطق البرمجي نيابةً عنك مما يقلل من الشيفرات البرمجية المتكررة التي من الممكن أن تتسبب بأخطاء، وتعطيك المكررات مرونةً أكبر في التعامل مع المنطق ذاته في الكثير من أنواع السلاسل وليس فقط <a href="https://academy.hsoub.com/programming/general/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-data-structures/" rel="">هياكل البيانات data structures</a> التي يمكنك الوصول إلى قيمها باستخدام دليل مثل الشعاع. دعنا نلقي نظرةً إلى كيفية تحقيق المكررات لكل هذا.
</p>

<h2>
	سمة Iterator وتابع next
</h2>

<p>
	تطبّق جميع المكررات السمة <code>Iterator</code> المُعرّفة في المكتبة القياسية، ويبدو تعريف السمة شيء مماثل لهذا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9813_12" style=""><span class="pln">pub trait </span><span class="typ">Iterator</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    type </span><span class="typ">Item</span><span class="pun">;</span><span class="pln">

    fn next</span><span class="pun">(&amp;</span><span class="pln">mut self</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Option</span><span class="pun">&lt;</span><span class="typ">Self</span><span class="pun">::</span><span class="typ">Item</span><span class="pun">&gt;;</span><span class="pln">

    </span><span class="com">// methods with default implementations elided</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لاحظ أن هذا التعريف يستخدم صيغتان جديدتان، هما: <code>type Item</code> و <code>Self::Item</code> اللتان تُعرِّفان نوعًا مترابطًا associated type مع هذه السمة. سنتحدث عن الأنواع المترابطة بالتفصيل لاحقًا، ويكفي للآن معرفتك أن هذه الشيفرة البرمجية تقول أن تطبيق السمة <code>Iterator</code> يتطلب منك تعريف نوع <code>Item</code> أيضًا وهذا النوع مُستخدم مثل نوع مُعاد من التابع <code>next</code>، وبكلمات أخرى، سيكون النوع <code>Item</code> هو النوع المُعاد من المكرّر.
</p>

<p>
	تتطلب السمة <code>Iterator</code> ممن يطبّقها فقط أن يعرّف تابعًا واحدًا هو <code>next</code>، الذي يُعيد عنصرًا واحدًا من المكرر كل مرة ضمن <code>Some</code> وعندما تنتهي العناصر (ينتهي التكرار)، يُعيد <code>None</code>.
</p>

<p>
	يمكننا استدعاء التابع <code>next</code> على المكررات مباشرةً، وتوضح الشيفرة 12 القيم المُعادة من الاستدعاءات المتعاقبة على المكرّر باستخدام <code>next</code> وهو المكرر الموجود في الشعاع.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9813_14" style=""><span class="pln">    </span><span class="com">#[test]</span><span class="pln">
    fn iterator_demonstration</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let v1 </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">];</span><span class="pln">

        let mut v1_iter </span><span class="pun">=</span><span class="pln"> v1</span><span class="pun">.</span><span class="pln">iter</span><span class="pun">();</span><span class="pln">

        assert_eq</span><span class="pun">!(</span><span class="pln">v1_iter</span><span class="pun">.</span><span class="pln">next</span><span class="pun">(),</span><span class="pln"> </span><span class="typ">Some</span><span class="pun">(&amp;</span><span class="lit">1</span><span class="pun">));</span><span class="pln">
        assert_eq</span><span class="pun">!(</span><span class="pln">v1_iter</span><span class="pun">.</span><span class="pln">next</span><span class="pun">(),</span><span class="pln"> </span><span class="typ">Some</span><span class="pun">(&amp;</span><span class="lit">2</span><span class="pun">));</span><span class="pln">
        assert_eq</span><span class="pun">!(</span><span class="pln">v1_iter</span><span class="pun">.</span><span class="pln">next</span><span class="pun">(),</span><span class="pln"> </span><span class="typ">Some</span><span class="pun">(&amp;</span><span class="lit">3</span><span class="pun">));</span><span class="pln">
        assert_eq</span><span class="pun">!(</span><span class="pln">v1_iter</span><span class="pun">.</span><span class="pln">next</span><span class="pun">(),</span><span class="pln"> </span><span class="typ">None</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	[الشيفرة 12: استدعاء التابع <code>next</code> على المكرر]
</p>

<p>
	لاحظ أننا احتجنا لإنشاء <code>v1_iter</code> متغيّر mutable، إذ يغيّر استدعاء التابع <code>next</code> على مكرر الحالة الداخلية التي يستخدمها المكرر لتتبع مكانه ضمن السلسلة، وبكلمات أخرى، <strong>تستهلك consumes</strong> الشيفرة البرمجية المكرّر، إذ يستهلك كل استدعاء للتابع <code>next</code> عنصرًا واحدًا من المكرر. لم يكن هناك أي حاجة لإنشاء <code>v1_iter</code> متغيّر عندما استخدمنا حلقة <code>for</code> لأن الحلقة أخذت ملكية ownership المكرر <code>v1_iter</code> وجعلته متغيّرًا ضمنيًا.
</p>

<p>
	لاحظ أيضًا أن القيم التي نحصل عليها من استدعاءات التابع <code>next</code> هي مراجع ثابتة immutable references للقيم الموجودة في الشعاع، ويعطينا التابع <code>iter</code> مكرًرًا على المراجع الثابتة. إذا أردنا إنشاء مكرر يأخذ ملكية <code>v1</code> ويُعيد القيم المملوكة فيمكننا استدعاء <code>into_iter</code> بدلًا من <code>iter</code>، ويمكننا بصورةٍ مماثلة استدعاء <code>iter_mut</code> بدلًا من <code>iter</code> إذا أردنا المرور على مراجع متغيّرة.
</p>

<h2>
	توابع تستهلك المكرر
</h2>

<p>
	للسمة <code>Iterator</code> العديد من التوابع في تطبيقها الافتراضي والموجودة في المكتبة القياسية، ويمكنك الاطّلاع على هذه التوابع عن طريق النظر إلى توثيق واجهة المكتبة القياسية البرمجية للسمة <code>Iterator</code>. تستدعي بعض هذه التوابع التابع <code>next</code> في تعريفها وهو السبب في ضرورة تطبيق التابع <code>next</code> عند تطبيق السمة <code>Iterator</code>.
</p>

<p>
	تُسمّى التوابع التي تستدعي التابع <code>next</code> بالمحوّلات المُستهلكة consuming adaptors لأن استدعائها يستهلك المكرر. يُعد تابع <code>sum</code> مثال على هذه التوابع، فهو يأخذ ملكية المكرر ويمرّ على عناصره بصورةٍ متتالية مع استدعاء <code>next</code> مما يتسبب باستهلاك المكرر، وبينما يمرّ على العناصر فهو يجمع كل عنصر إلى قيمة كلية، ثم يعيد القيمة الكلية في النهاية. تحتوي الشيفرة 13 على اختبار يوضح استخدام التابع <code>sum</code>:
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9813_16" style=""><span class="pln">    </span><span class="com">#[test]</span><span class="pln">
    fn iterator_sum</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let v1 </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">];</span><span class="pln">

        let v1_iter </span><span class="pun">=</span><span class="pln"> v1</span><span class="pun">.</span><span class="pln">iter</span><span class="pun">();</span><span class="pln">

        let total</span><span class="pun">:</span><span class="pln"> i32 </span><span class="pun">=</span><span class="pln"> v1_iter</span><span class="pun">.</span><span class="pln">sum</span><span class="pun">();</span><span class="pln">

        assert_eq</span><span class="pun">!(</span><span class="pln">total</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	[الشيفرة 13: استدعاء التابع <code>sum</code> للحصول على القيمة الكلية لجميع العناصر في المكرر]
</p>

<p>
	لا يُسمح لنا باستخدام <code>v1_iter</code> بعد استدعاء <code>sum</code> لأن <code>sum</code> يأخذ ملكية المكرّر الذي نستدعي <code>sum</code> عليه.
</p>

<h2>
	التوابع التي تنشئ مكررات أخرى
</h2>

<p>
	محولات المكرر iterator adaptors هي توابع مُعرّفة على السمة <code>Iterator</code> ولا تستهلك المكرر، بل تُنشئ مكررات مختلفة بدلًا من ذلك عن طريق تغيير بعض خصائص المكرر الأصل.
</p>

<p>
	توضح الشيفرة 14 مثالًا عن استدعاء تابع محول مكرر <code>map</code> وهو تابع يأخذ مغلّفًا closure ويستدعيه على كل من العناصر بصورةٍ متتالية. يُعيد التابع <code>map</code> مكررًا جديدًا يُنشئ عناصرًا مُعدّلٌ عليها، ويُنشئ المغلف في هذه الحالة مكررًا جديدًا يزيد قيمة عناصره بمقدار 1 لكل عنصر في الشعاع:
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9813_18" style=""><span class="pln">    let v1</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vec</span><span class="str">&lt;i32&gt;</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">];</span><span class="pln">

    v1</span><span class="pun">.</span><span class="pln">iter</span><span class="pun">().</span><span class="typ">map</span><span class="pun">(|</span><span class="pln">x</span><span class="pun">|</span><span class="pln"> x </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span></pre>

<p>
	[الشيفرة 14: استدعاء محول المكرر <code>map</code> لإنشاء مكرر جديد]
</p>

<p>
	إلا أن الشيفرة البرمجية السابقة تعطينا إنذارًا:
</p>

<pre class="ipsCode">$ cargo run
   Compiling iterators v0.1.0 (file:///projects/iterators)
warning: unused `Map` that must be used
 --&gt; src/main.rs:4:5
  |
4 |     v1.iter().map(|x| x + 1);
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_must_use)]` on by default
  = note: iterators are lazy and do nothing unless consumed

warning: `iterators` (bin "iterators") generated 1 warning
    Finished dev [unoptimized + debuginfo] target(s) in 0.47s
     Running `target/debug/iterators`
</pre>

<p>
	لا تفعل الشيفرة 14 أي شيء، إذ أن المغلف الذي حددناه لا يُستدعى أبدًا، ويذكرنا الإنذار بسبب ذلك: إذ أن محولات المكرر كسولة ونحتاج لاستهلاك المكرر هنا.
</p>

<p>
	نستخدم التابع <code>collect</code> لتصحيح هذا الإنذار واستهلاك المكرر وهو ما استخدمناه في (<a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-command-line-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AF%D8%AE%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%B1%D8%AC-r1969/" rel="">الشيفرة 1</a> من فصل سابق) باستخدام <code>env::args</code>، إذ يستهلك هذا التابع المكرر ويجمّع القيم الناتجة إلى نوع بيانات تجميعة collection data type.
</p>

<p>
	نجمع في الشيفرة 15 النتائج من عملية المرور على المكرر والمُعادة من استدعاء التابع <code>map</code> على الشعاع، وسيحتوي هذا الشعاع في نهاية المطاف على جميع عناصره الموجودة مع زيادة على قيمة كل منها بمقدار 1.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9813_21" style=""><span class="pln">    let v1</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vec</span><span class="str">&lt;i32&gt;</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">];</span><span class="pln">

    let v2</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="pln">_</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> v1</span><span class="pun">.</span><span class="pln">iter</span><span class="pun">().</span><span class="typ">map</span><span class="pun">(|</span><span class="pln">x</span><span class="pun">|</span><span class="pln"> x </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">).</span><span class="pln">collect</span><span class="pun">();</span><span class="pln">

    assert_eq</span><span class="pun">!(</span><span class="pln">v2</span><span class="pun">,</span><span class="pln"> vec</span><span class="pun">![</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">]);</span></pre>

<p>
	[الشيفرة 15: استدعاء التابع <code>map</code> لإنشاء مكرر جديد ومن ثم استدعاء التابع <code>collect</code> لاستهلاك المكرر الجديد وإنشاء شعاع]
</p>

<p>
	يمكننا تحديد أي عملية نريد إنجازها على كل عنصر بالنظر إلى أن <code>map</code> تتلقى مغلفًا بمثابة وسيط لها. هذا مثال عظيم عن كيفية إنجاز مهام معقدة بطريقة مقروءة، ولأن جميع المكررات كسولة، فهذا يعني أنك بحاجة استدعاء واحد من توابع المحولات المستهلكة للحصول على نتائج استدعاءات محولات المكرر.
</p>

<h2>
	استخدام المغلفات التي تحصل على القيم من بيئتها
</h2>

<p>
	تأخذ الكثير من محولات المكرر المغلفات مثل وسطاء لها، وستحصل هذه المغلفات التي تُحدد مثل وسطاء لمحولات المكرر على قيم من بيئتها غالبًا.
</p>

<p>
	نستخدم في هذا المثال التابع <code>filter</code> الذي يأخذ مغلّفًا، ويحصل المغلف على عنصر من المكرر ويُعيد <code>bool</code>، وتُضمّن هذه القيمة في التكرار المُنشئ بواسطة <code>filter</code> إذا أعاد المغلف القيمة <code>true</code>، وإذا أعاد المكرر القيمة <code>false</code> فلن تُضمَّن القيمة.
</p>

<p>
	نستخدم في الشيفرة 16 التابع <code>filter</code> بمغلّف يحصل على المتغير <code>shoe_size</code> من بيئته للمرور على تجميعة من نسخ instances الهيكل‏ <code>Shoe</code>، وسيُعيد فقط الأحذية ذات مقاس محدد.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9813_23" style=""><span class="com">#[derive(PartialEq, Debug)]</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Shoe</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    size</span><span class="pun">:</span><span class="pln"> u32</span><span class="pun">,</span><span class="pln">
    style</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

fn shoes_in_size</span><span class="pun">(</span><span class="pln">shoes</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">Shoe</span><span class="pun">&gt;,</span><span class="pln"> shoe_size</span><span class="pun">:</span><span class="pln"> u32</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">Shoe</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    shoes</span><span class="pun">.</span><span class="pln">into_iter</span><span class="pun">().</span><span class="pln">filter</span><span class="pun">(|</span><span class="pln">s</span><span class="pun">|</span><span class="pln"> s</span><span class="pun">.</span><span class="pln">size </span><span class="pun">==</span><span class="pln"> shoe_size</span><span class="pun">).</span><span class="pln">collect</span><span class="pun">()</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">#[cfg(test)]</span><span class="pln">
mod tests </span><span class="pun">{</span><span class="pln">
    use super</span><span class="pun">::*;</span><span class="pln">

    </span><span class="com">#[test]</span><span class="pln">
    fn filters_by_size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let shoes </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![</span><span class="pln">
            </span><span class="typ">Shoe</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln">
                style</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"sneaker"</span><span class="pun">),</span><span class="pln">
            </span><span class="pun">},</span><span class="pln">
            </span><span class="typ">Shoe</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">13</span><span class="pun">,</span><span class="pln">
                style</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"sandal"</span><span class="pun">),</span><span class="pln">
            </span><span class="pun">},</span><span class="pln">
            </span><span class="typ">Shoe</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln">
                style</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"boot"</span><span class="pun">),</span><span class="pln">
            </span><span class="pun">},</span><span class="pln">
        </span><span class="pun">];</span><span class="pln">

        let in_my_size </span><span class="pun">=</span><span class="pln"> shoes_in_size</span><span class="pun">(</span><span class="pln">shoes</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">);</span><span class="pln">

        assert_eq</span><span class="pun">!(</span><span class="pln">
            in_my_size</span><span class="pun">,</span><span class="pln">
            vec</span><span class="pun">![</span><span class="pln">
                </span><span class="typ">Shoe</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                    size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln">
                    style</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"sneaker"</span><span class="pun">)</span><span class="pln">
                </span><span class="pun">},</span><span class="pln">
                </span><span class="typ">Shoe</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                    size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln">
                    style</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"boot"</span><span class="pun">)</span><span class="pln">
                </span><span class="pun">},</span><span class="pln">
            </span><span class="pun">]</span><span class="pln">
        </span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	[الشيفرة 16: استخدام التابع <code>filter</code> مع مغلف يحصل على القيمة <code>shoe_size</code>]
</p>

<p>
	تأخذ الدالة <code>shoes_in_size</code> ملكية شعاع الأحذية وقياس الحذاء مثل معاملات، وتُعيد شعاعًا يحتوي على الأحذية بالمقاس المحدد.
</p>

<p>
	نستدعي <code>into_iter</code> في متن الدالة <code>shoes_in_size</code> لإنشاء مكرر يأخذ ملكية الشعاع، ثم نستدعي <code>filter</code> لتحويل المكرّر إلى مكرر جديد يحتوي فقط على عناصر يُعيد منها المغلّف القيمة <code>true</code>.
</p>

<p>
	يحصل المغلف على المعامل <code>shoe_size</code> من البيئة ويقارنه مع قيمة كل مقاس حذاء مع إبقاء الأحذية التي يتطابق مقاسها، وأخيرًا يجمع استدعاء <code>collect</code> القيم المُعادة بواسطة محول المكرر إلى شعاع يُعاد من الدالة.
</p>

<p>
	يوضح هذا الاختبار أنه عندما نستدعي <code>shoes_in_size</code>، فنحن نحصل فقط على الأحذية التي تتطابق مقاساتها مع القيمة التي حددناها.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch13-00-functional-features.html" rel="external nofollow">Functional Language Features: Iterators and Closures</a> من كتاب <a href="https://doc.rust-lang.org/stable/book/title-page.html/" rel="external nofollow">The Rust Programming Language</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D9%83%D8%B1%D8%B1%D8%A7%D8%AA-iterators-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1994/" rel="">استخدام المكررات Iterators في تطبيق سطر أوامر بلغة رست</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%BA%D9%84%D9%81%D8%A7%D8%AA-closures-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1980/" rel="">المغلفات closures في لغة رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-data-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1780/" rel="">أنواع البيانات Data Types في لغة رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A8%D8%B3%D9%8A%D8%B1-%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%B1%D8%A7%D8%B3%D8%AA-rust-r1785/" rel="">التحكم بسير تنفيذ برامج راست Rust</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1993</guid><pubDate>Sat, 03 Jun 2023 13:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x645;&#x63A;&#x644;&#x641;&#x627;&#x62A; closures &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A; Rust</title><link>https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%BA%D9%84%D9%81%D8%A7%D8%AA-closures-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1980/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_06/-closures----Rust.png.3c8d75b23ebf8dac5679f369ae869de2.png" /></p>
<p>
	تمثّل المغلّفات في لغة رست دوالًا مجهولة anonymous يمكنك حفظها في متغير أو تمريرها مثل وسيط إلى دالة أخرى، ويمكنك إنشاء مغلف في مكان ما، ثم استدعاءه من مكان آخر ليُفَّذ بحسب سياق المكان، وعلى عكس الدوال فالمغلفات يمكنها الوصول إلى القيم الموجودة في النطاق المعرفة بها، وسنوضح كيف تسمح لنا مزايا المغلفات بإعادة استخدام شيفرتنا البرمجية وتخصيص سلوكها.
</p>

<h2>
	الحصول على المعلومات من البيئة باستخدام المغلفات
</h2>

<p>
	سنفحص أولًا كيفية استخدام المغلفات للحصول على القيم من البيئة التي عرّفناها فيها لاستخدام لاحق. إليك حالة استخدام ممكنة: لدينا شركة لبيع القمصان ونمنح شخصًا ما على قائمة مراسلة البريد الإلكتروني قميصًا حصريًا بين الحين والآخر مثل ترويج لشركتنا، ويمكن أن يُضيف الأشخاص على قائمة المراسلة لونهم المفضل إلى ملفهم بصورةٍ اختيارية، وإذا حدّد الشخص الذي سيحصل على قميص مجاني لونه المفضل فإنه يحصل على هذا اللون تحديدًا وإلا فإنه يحصل على اللون المتواجد بكثرة في المخزن.
</p>

<p>
	هناك عدة طرق لتطبيق ذلك، إذ يمكننا على سبيل المثال استخدام معدّد enum يدعى <code>ShirtColor</code> يحتوي على متغايرين variants هما‏ <code>Red</code> و <code>Blue</code> (حددنا لونين فقط للبساطة). نمثّل مخزن الشركة باستخدام الهيكل‏ <code>Inventory</code> الذي يحتوي على حقل يدعى <code>shirts</code> يحتوي على النوع <code>Vec&lt;ShirtColor&gt;‎</code>، الذي يمثّل لون القميص الموجود حاليًا في المخزن. يحصل التابع <code>giveaway</code> المُعرّف في <code>Inventory</code> على لون القميص المفضّل الاختياري للمستخدم من المستخدمين الرابحين القميص مجانًا ويُعيد لون القميص الذي سيحصل عليه المستخدم. التطبيق لكل ما سبق ذكره موضح في الشيفرة 1:
</p>

<p>
	اسم الملف: src/main.rss
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5937_6" style=""><span class="com">#[derive(Debug, PartialEq, Copy, Clone)]</span><span class="pln">
</span><span class="kwd">enum</span><span class="pln"> </span><span class="typ">ShirtColor</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Red</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">Blue</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Inventory</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    shirts</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">ShirtColor</span><span class="pun">&gt;,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

impl </span><span class="typ">Inventory</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn giveaway</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> user_preference</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Option</span><span class="pun">&lt;</span><span class="typ">ShirtColor</span><span class="pun">&gt;)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">ShirtColor</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        user_preference</span><span class="pun">.</span><span class="pln">unwrap_or_else</span><span class="pun">(||</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">most_stocked</span><span class="pun">())</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    fn most_stocked</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">ShirtColor</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let mut num_red </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
        let mut num_blue </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">for</span><span class="pln"> color in </span><span class="pun">&amp;</span><span class="pln">self</span><span class="pun">.</span><span class="pln">shirts </span><span class="pun">{</span><span class="pln">
            match color </span><span class="pun">{</span><span class="pln">
                </span><span class="typ">ShirtColor</span><span class="pun">::</span><span class="typ">Red</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> num_red </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
                </span><span class="typ">ShirtColor</span><span class="pun">::</span><span class="typ">Blue</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> num_blue </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> num_red </span><span class="pun">&gt;</span><span class="pln"> num_blue </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">ShirtColor</span><span class="pun">::</span><span class="typ">Red</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">ShirtColor</span><span class="pun">::</span><span class="typ">Blue</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let store </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Inventory</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        shirts</span><span class="pun">:</span><span class="pln"> vec</span><span class="pun">![</span><span class="typ">ShirtColor</span><span class="pun">::</span><span class="typ">Blue</span><span class="pun">,</span><span class="pln"> </span><span class="typ">ShirtColor</span><span class="pun">::</span><span class="typ">Red</span><span class="pun">,</span><span class="pln"> </span><span class="typ">ShirtColor</span><span class="pun">::</span><span class="typ">Blue</span><span class="pun">],</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">

    let user_pref1 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Some</span><span class="pun">(</span><span class="typ">ShirtColor</span><span class="pun">::</span><span class="typ">Red</span><span class="pun">);</span><span class="pln">
    let giveaway1 </span><span class="pun">=</span><span class="pln"> store</span><span class="pun">.</span><span class="pln">giveaway</span><span class="pun">(</span><span class="pln">user_pref1</span><span class="pun">);</span><span class="pln">
    println</span><span class="pun">!(</span><span class="pln">
        </span><span class="str">"The user with preference {:?} gets {:?}"</span><span class="pun">,</span><span class="pln">
        user_pref1</span><span class="pun">,</span><span class="pln"> giveaway1
    </span><span class="pun">);</span><span class="pln">

    let user_pref2 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">None</span><span class="pun">;</span><span class="pln">
    let giveaway2 </span><span class="pun">=</span><span class="pln"> store</span><span class="pun">.</span><span class="pln">giveaway</span><span class="pun">(</span><span class="pln">user_pref2</span><span class="pun">);</span><span class="pln">
    println</span><span class="pun">!(</span><span class="pln">
        </span><span class="str">"The user with preference {:?} gets {:?}"</span><span class="pun">,</span><span class="pln">
        user_pref2</span><span class="pun">,</span><span class="pln"> giveaway2
    </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 1: شيفرة توزيع القمصان للمستخدمين
</p>

<p>
	يحتوي المتغير <code>store</code> المعرف في الدالة <code>main</code> على قميصين أحدهما باللون الأزرق والآخر باللون الأحمر للتوزيع ضمن حملة التسويق هذه. نستدعي التابع <code>giveaway</code> للمستخدم الذي يفضّل القميص الأحمر وللمستخدم الذي ليس لديه أي تفضيل معين.
</p>

<p>
	يمكن تطبيق الشيفرة البرمجية بمختلف الطرق، إلا أننا نركز هنا على استخدام المغلفات، لذا فقد التزمنا بالمفاهيم التي تعلمتها مسبقًا باستثناء ما بداخل التابع <code>giveaway</code> الذي يستخدم مغلّفًا. نحصل على تفضيل المستخدم مثل معامل من النوع <code>Option&lt;ShirtColor&gt;‎</code> في التابع <code>giveaway</code> ونستدعي التابع <code>unwrap_or_else</code> على <code>user_preference</code>. التابع <code>unwrap_or_else</code> على النوع <code>Option&lt;T&gt;‎</code> مُعرّف في المكتبة القياسية ويأخذ وسيطًا واحدًا ألا وهو مغلف دون أي وسطاء يعيد القيمة <code>T</code> (النوع ذاته المُخزن في المتغاير <code>Some</code> داخل النوع <code>Option&lt;T&gt;‎</code>، وفي هذه الحالة <code>ShirtColor</code>). إذا كان النوع <code>Option&lt;T&gt;‎</code> هو المتغاير <code>Some</code>، سيُعيد التابع <code>unwrap_or_else</code> القيمة الموجودة داخل <code>Some</code>، وإذا كان المتغاير داخل النوع <code>Option&lt;T&gt;‎</code> هو <code>None</code>، سيستدعي التابع المغلف ويُعيد القيمة المُعادة من المغلف.
</p>

<p>
	نحدد تعبير المغلف بالشكل <code>‎|| self.most_stocked()‎</code> مثل وسيط للتابع <code>unwrap_or_else</code>. لا يأخذ هذا المغلف أي معاملات، إذ نضع المعاملات بين الخطين العموديين إذا احتوى المغلف على معاملات. يستدعي متن المغلف التابع <code>self.most_stocked()‎</code>، ونعرّف هنا المغلف بحيث يُقيّم تطبيق <code>unwrap_or_else</code> المغلف لاحقًا إذا احتجنا للنتيجة.
</p>

<p>
	يطبع تنفيذ الشيفرة البرمجية السابقة الخرج التالي:
</p>

<pre class="ipsCode">$ cargo run
   Compiling shirt-company v0.1.0 (file:///projects/shirt-company)
    Finished dev [unoptimized + debuginfo] target(s) in 0.27s
     Running `target/debug/shirt-company`
The user with preference Some(Red) gets Red
The user with preference None gets Blue
</pre>

<p>
	الجانب المثير للاهتمام هنا هو أننا نمرر مغلفًا يستدعي <code>self.most_stocked()‎</code> على نسخة <code>Inventory</code> الحالية. لا تحتاج المكتبة القياسية لمعرفة أي شيء بخصوص النوعين <code>Inventory</code> أو <code>ShirtColor</code> الذين عرّفناهما، أو المنطق الذي نريد استخدامه في هذه الحالة، إذ أن المغلف يحصل على مرجع ثابث immutable reference إلى <code>self</code> الخاصة بنسخة <code>Inventory</code>، ثم يمرره إلى الشيفرة البرمجية التي كتبناها داخل التابع <code>unwrap_or_else</code>. إذا قارنّا هذه العملية <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D9%88%D8%A7%D8%A8%D8%B9-methods-%D8%B6%D9%85%D9%86-%D8%A7%D9%84%D9%87%D9%8A%D8%A7%D9%83%D9%84-structs-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1850/" rel="">بالتوابع</a>، فالتوابع غير قادرة على الحصول على هذه المعلومات من البيئة بالطريقة ذاتها.
</p>

<h2>
	استنتاج نوع المغلف وتوصيفه
</h2>

<p>
	هناك المزيد من الاختلافات بين <a href="https://academy.hsoub.com/programming/rust/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-functions-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%82%D8%A7%D8%AA-comments-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%A7%D8%B3%D8%AA-rust-r1781/" rel="">الدوال</a> والمغلفات، إذ لا تتطلب المغلفات منك عادةً توصيف أنواع المعاملات، أو نوع القيمة المُعادة مثلما تتطلب الدوال <code>fn</code> ذلك، والسبب في ضرورة تحديد الأنواع في الدوال هو لأن الأنواع جزءٌ من واجهة صريحة مكشوفة لمستخدميك وتعريف الواجهة بصورةٍ دقيقة مهمٌ للتأكد من أن الجميع متفقٌ على أنواع القيم التي تستخدمها الدالة وتعيدها، بينما لا تُستخدم المغلّفات في الواجهات المكشوفة بهذه الطريقة، إذ أنها تُخزّن في متغيرات دون تسميتها وكشفها لمستخدمي مكتبتك.
</p>

<p>
	تكون المغلفات عادةً قصيرة وترتبط بسياق معين ضيق بدلًا من حالة عامة اعتباطية، ويمكن للمصرّف في هذا السياق المحدود استنتاج أنواع المعاملات والقيمة المعادة بصورةٍ مشابهة لاستنتاجه لمعظم <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%AF%D9%8A%D9%84-%D8%B9%D9%84%D9%8A%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1779/" rel="">أنواع المتغيرات</a>، وهناك حالات نادرة يحتاج فيها المصرف وجود توصيف للأنواع في المغلفات أيضًا.
</p>

<p>
	يمكننا إضافة توصيف النوع إذا أردنا لزيادة دقة ووضوح المغلف كما هو الحال عند تعريف المتغيرات، إلا أن هذه الطريقة تتطلب كتابةً أطول غير ضرورية. يبدو توصيف الأنواع في المغلفات مثل التعريف الموجود في الشيفرة 2، ونعرّف في هذا المثال مغلّفًا في النقطة التي نمرّر فيها وسيطًا كما فعلنا في الشيفرة 1.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5937_8" style=""><span class="pln">    let expensive_closure </span><span class="pun">=</span><span class="pln"> </span><span class="pun">|</span><span class="pln">num</span><span class="pun">:</span><span class="pln"> u32</span><span class="pun">|</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> u32 </span><span class="pun">{</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"calculating slowly..."</span><span class="pun">);</span><span class="pln">
        thread</span><span class="pun">::</span><span class="pln">sleep</span><span class="pun">(</span><span class="typ">Duration</span><span class="pun">::</span><span class="pln">from_secs</span><span class="pun">(</span><span class="lit">2</span><span class="pun">));</span><span class="pln">
        num
    </span><span class="pun">};</span></pre>

<p style="text-align: center;">
	الشيفرة 2: إضافة توصيف اختياري لأنواع المعاملات والقيمة المُعادة في المغلف
</p>

<p>
	تبدو طريقة كتابة المغلفات مشابهة لطريقة كتابة الدوال بعد إضافة توصيف النوع، إذ نعرّف هنا دالةً تجمع 1 إلى المعامل ومغلّفًا بالسلوك ذاته للمقارنة بين الاثنين، ويوضح ذلك كيف أن طريقة كتابة المغلفات مشابهة لطريقة كتابة الدوال باستثناء استخدام الرمز <code>|</code> وكمية الصياغة الاختيارية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5937_10" style=""><span class="pln">fn  add_one_v1   </span><span class="pun">(</span><span class="pln">x</span><span class="pun">:</span><span class="pln"> u32</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> u32 </span><span class="pun">{</span><span class="pln"> x </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
let add_one_v2 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">|</span><span class="pln">x</span><span class="pun">:</span><span class="pln"> u32</span><span class="pun">|</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> u32 </span><span class="pun">{</span><span class="pln"> x </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
let add_one_v3 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">|</span><span class="pln">x</span><span class="pun">|</span><span class="pln">             </span><span class="pun">{</span><span class="pln"> x </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
let add_one_v4 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">|</span><span class="pln">x</span><span class="pun">|</span><span class="pln">               x </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pln">  </span><span class="pun">;</span></pre>

<p>
	يوضح السطر الأول تعريف دالة، بينما يوضح السطر الثاني تعريف مغلف موصّف بالكامل، ثمّ نزيل في السطر الثالث التوصيف من تعريف المغلف، ونزيل في السطر الرابع الأقواس الاختيارية لأن محتوى المغلف يتألف من تعبير واحد، وجميع التعاريف السابقة تعاريف صالحة الاستخدام تمنحنا السلوك ذاته عند استدعائها. يتطلب السطران <code>add_one_v3</code> و <code>add_one_v4</code> تقييم قيمة المغلف حتى يجري تصريفهما، لأن الأنواع يجب أن تُسنتج من خلال استخدامهما وهذا الأمر مشابه لحاجة السطر <code>let v = Vec::new();‎</code> لتوصيف النوع أو إضافة قيم من نوع ما إلى <code>Vec</code>، بحيث تستطيع <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-%D8%A7%D9%84%D8%A8%D8%AF%D8%A7%D9%8A%D8%A7%D8%AA-r1764/" rel="">رست</a> استنتاج النوع.
</p>

<p>
	يستنتج المصرف نوعًا واحدًا ثابتًا لكل من المعاملات في حال تعريف المغلفات، إضافةً إلى النوع المُعاد منها. على سبيل المثال، توضح الشيفرة 3 تعريف مغلف قصير يُعيد القيمة التي يتلقاها مثل معاملٍ له، وهذا المغلف ليس مفيدًا جدًا إلا أن هدفه توضيحي. لاحظ أننا لم نضِف أي توصيف للنوع في التعريف وبالتالي يمكننا استدعاء المغلف باستخدام أي نوع، وهو ما فعلناه في الشيفرة 3، إذ استدعينا المغلّف في المرة الأولى باستخدام النوع <code>String</code>، إلا أننا حصلنا على <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1920/" rel="">خطأ</a> عند استدعائنا للمغلف <code>example_closure</code> باستخدام قيمة عدد صحيح integer.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5937_12" style=""><span class="pln">    let example_closure </span><span class="pun">=</span><span class="pln"> </span><span class="pun">|</span><span class="pln">x</span><span class="pun">|</span><span class="pln"> x</span><span class="pun">;</span><span class="pln">

    let s </span><span class="pun">=</span><span class="pln"> example_closure</span><span class="pun">(</span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"hello"</span><span class="pun">));</span><span class="pln">
    let n </span><span class="pun">=</span><span class="pln"> example_closure</span><span class="pun">(</span><span class="lit">5</span><span class="pun">);</span></pre>

<p style="text-align: center;">
	الشيفرة 3: محاولة استدعاء مغلف يُستنتج نوعه باستخدام نوعين مختلفين
</p>

<p>
	يعرض لنا <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%81%D8%B5%D9%84-%D8%A7%D9%84%D8%A3%D9%88%D9%84-%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%AA%D8%B5%D8%B1%D9%8A%D9%81-compilation-%D9%81%D9%8A-%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r976/" rel="">المصرّف</a> الخطأ التالي:
</p>

<pre class="ipsCode">$ cargo run
   Compiling closure-example v0.1.0 (file:///projects/closure-example)
error[E0308]: mismatched types
 --&gt; src/main.rs:5:29
  |
5 |     let n = example_closure(5);
  |             --------------- ^- help: try using a conversion method: `.to_string()`
  |             |               |
  |             |               expected struct `String`, found integer
  |             arguments to this function are incorrect
  |
note: closure parameter defined here
 --&gt; src/main.rs:2:28
  |
2 |     let example_closure = |x| x;
  |                            ^

For more information about this error, try `rustc --explain E0308`.
error: could not compile `closure-example` due to previous error
</pre>

<p>
	استنبط المصرّف نوع <code>x</code> المُمرّر بكونه سلسلة نصية <code>String</code> عندما رأى أن أول استخدام للمغلف <code>example_closure</code> كان باستخدام قيمة <code>String</code>، كما استنبط القيمة المعادة بالنوع <code>String</code>. تُقيّد هذه الأنواع في المغلف <code>example_closure</code> من تلك النقطة فصاعدًا وسنحصل على خطأ، إذا حاولنا استخدام نوع مختلف بعد ذلك في المغلف ذاته.
</p>

<h2>
	الحصول على المراجع أو نقل الملكية
</h2>

<p>
	يمكن أن تحصل المغلفات على القيم من البيئة بثلاث طرق وهي مرتبطة بالطرق الثلاث التي تستطيع فيها الدالة الحصول على القيم على أنها معاملاتها: الاستعارة الثابتة أو الاستعارة المتغيّرة أو أخذ <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D9%84%D9%83%D9%8A%D8%A9-ownership-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1786/" rel="">الملكية</a>، ويحدد المغلف واحدًا من هذه الطرق الثلاث حسب القيم الموجودة في متن الدالة.
</p>

<p>
	نعرّف في الشيفرة 4 مغلفًا يحصل على مرجع ثابت لشعاع يدعى <code>list</code> لأنه يحتاج فقط للمراجع الثابتة لطباعة القيمة:
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5937_15" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let </span><span class="typ">list</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">];</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"Before defining closure: {:?}"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">list</span><span class="pun">);</span><span class="pln">

    let only_borrows </span><span class="pun">=</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"From closure: {:?}"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">list</span><span class="pun">);</span><span class="pln">

    println</span><span class="pun">!(</span><span class="str">"Before calling closure: {:?}"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">list</span><span class="pun">);</span><span class="pln">
    only_borrows</span><span class="pun">();</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"After calling closure: {:?}"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">list</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 4: تعريف مغلف واستدعاءه، بحيث يحصل هذا المغلف على مرجع ثابت
</p>

<p>
	يوضح هذا المثال أيضًا أنه من الممكن للمتغير أن يرتبط بتعريف مغلف، ويمكننا لاحقًا استدعاء المغلف باستخدام اسم المتغير والقوسين وكأن اسم المتغير يمثل اسم دالة.
</p>

<p>
	يمكن الوصول للشعاع <code>list</code> من الشيفرة البرمجية قبل تعريف المغلف وبعد تعريفه، شرط أن يكون قبل استدعاء المغلف وبعد استدعاء المغلف، وذلك لأنه من الممكن وجود عدّة مراجع ثابتة للشعاع <code>list</code> في ذات الوقت، وتُصرَّف الشيفرة البرمجية بنجاح وتُنفَّذ وتطبع التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5937_17" style=""><span class="pln">$ cargo run
   </span><span class="typ">Compiling</span><span class="pln"> closure</span><span class="pun">-</span><span class="pln">example v0</span><span class="pun">.</span><span class="lit">1.0</span><span class="pln"> </span><span class="pun">(</span><span class="pln">file</span><span class="pun">:</span><span class="com">///projects/closure-example)</span><span class="pln">
    </span><span class="typ">Finished</span><span class="pln"> dev </span><span class="pun">[</span><span class="pln">unoptimized </span><span class="pun">+</span><span class="pln"> debuginfo</span><span class="pun">]</span><span class="pln"> target</span><span class="pun">(</span><span class="pln">s</span><span class="pun">)</span><span class="pln"> in </span><span class="lit">0.43s</span><span class="pln">
     </span><span class="typ">Running</span><span class="pln"> </span><span class="pun">`</span><span class="pln">target</span><span class="pun">/</span><span class="pln">debug</span><span class="pun">/</span><span class="pln">closure</span><span class="pun">-</span><span class="pln">example</span><span class="pun">`</span><span class="pln">
</span><span class="typ">Before</span><span class="pln"> defining closure</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">]</span><span class="pln">
</span><span class="typ">Before</span><span class="pln"> calling closure</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">]</span><span class="pln">
</span><span class="typ">From</span><span class="pln"> closure</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">]</span><span class="pln">
</span><span class="typ">After</span><span class="pln"> calling closure</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">]</span></pre>

<p>
	نعدّل في الشيفرة 5 متن المغلف بحيث نضيف عنصرًا إلى الشعاع <code>list</code>، وبالتالي يحصل المغلف الآن على مرجع متغيّر:
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5937_19" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let mut </span><span class="typ">list</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">];</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"Before defining closure: {:?}"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">list</span><span class="pun">);</span><span class="pln">

    let mut borrows_mutably </span><span class="pun">=</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> </span><span class="typ">list</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="lit">7</span><span class="pun">);</span><span class="pln">

    borrows_mutably</span><span class="pun">();</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"After calling closure: {:?}"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">list</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 5: تعريف واستدعاء مغلف يحتوي على مرجع متغيّر
</p>

<p>
	تُصرَّف الشيفرة البرمجية بنجاح وتُنفَّذ ونحصل على الخرج التالي:
</p>

<pre class="ipsCode">$ cargo run
   Compiling closure-example v0.1.0 (file:///projects/closure-example)
    Finished dev [unoptimized + debuginfo] target(s) in 0.43s
     Running `target/debug/closure-example`
Before defining closure: [1, 2, 3]
After calling closure: [1, 2, 3, 7]
</pre>

<p>
	لاحظ أننا لا نستخدم <code>println!‎</code> بين تعريف المغلف <code>borrows_mutably</code> واستدعائه، إذ يحصل المغلف على مرجع متغيّر للشعاع <code>list</code> عند تعريفه، ولا نستخدم المغلف مجددًا بعد استدعائه لذا تنتهي الاستعارة المتغيرة عندها. لا يُسمح بطباعة مرجع متغيّر بين تعريف المغلف واستدعائه، إذ لا يُسمح بوجود أي عمليات استعارة أخرى عند وجود استعارة متغيّرة. حاول إضافة استدعاء للماكرو <code>println!‎</code> لترى رسالة الخطأ التي تظهر لك.
</p>

<p>
	إذا أردت إجبار المغلف على أخذ ملكية القيم التي يستخدمها في البيئة على الرغم من أن متن المغلف لا يتطلب أخذ الملكية، فيمكنك استخدام الكلمة المفتاحية <code>move</code> قبل قائمة المعاملات.
</p>

<p>
	هذه الطريقة مفيدة خصوصًا عند تمرير مغلف <a href="https://academy.hsoub.com/programming/java/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1483/" rel="">لخيط thread</a> جديد لنقل <a href="https://academy.hsoub.com/programming/rust/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-data-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1780/" rel="">البيانات</a>، بحيث تُمتلك بواسطة الخيط الجديد. سنناقش الخيوط بالتفصيل وسبب استخدامنا لها لاحقًا، لكن دعنا للوقت الحالي نستكشف عملية إضافة خيط جديد باستخدام مغلّف يحتاج الكلمة المفتاحية <code>move</code>. توضح الشيفرة 6 إصدارًا عن الشيفرة 4، إلا أننا نطبع الشعاع هنا في خيط جديد بدلًا من الخيط الرئيسي:
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5937_21" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">thread</span><span class="pun">;</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let </span><span class="typ">list</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">];</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"Before defining closure: {:?}"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">list</span><span class="pun">);</span><span class="pln">

    thread</span><span class="pun">::</span><span class="pln">spawn</span><span class="pun">(</span><span class="pln">move </span><span class="pun">||</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"From thread: {:?}"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">list</span><span class="pun">))</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">join</span><span class="pun">()</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 6: استخدام <code>move</code> لإجبار خيط المغلف على أخذ ملكية <code>list</code>
</p>

<p>
	نُنشئ خيطًا جديدًا وذلك بمنح الخيط مغلف ليعمل به مثل وسيط، ويطبع متن المغلف القائمة. يحصل المغلف في الشيفرة 4 على <code>list</code> باستخدام مرجع ثابت لأنها تُعد أدنى درجات الوصول المطلوبة لطباعة محتويات <code>list</code>، إلا أننا في هذا المثال نحدد أن <code>list</code> يجب أن تُنقل إلى المغلف بإضافة الكلمة المفتاحية <code>move</code> في بداية تعريف المغلّف على الرغم من أن متن المغلف يتطلب فقط مرجعًا ثابتًا. يمكن للخيط الجديد أن ينتهي من التنفيذ قبل انتهاء الخيط الرئيسي من التنفيذ أو أن ينتهي الخيط الرئيسي أولًا، وإذا احتفظ الخيط الرئيسي بملكية <code>list</code> وانتهى من التنفيذ قبل الخيط الجديد وأسقط <code>list</code> فهذا يعني أن المرجع الثابت في الخيط الجديد سيكون غير صالحًا، ولذلك يتطلب المصرف نقل <code>list</code> إلى المغلف في الخيط الجديد بحيث يبقى <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%B1%D8%A7%D8%AC%D8%B9-references-%D9%88%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D8%A7%D8%B1%D8%A9-borrowing-%D9%88%D8%A7%D9%84%D8%B4%D8%B1%D8%A7%D8%A6%D8%AD-slices-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1787/" rel="">المرجع</a> صالحًا داخله. جرّب إزالة الكلمة المفتاحية <code>move</code> أو استخدام <code>list</code> في الخيط الرئيسي بعد تعريف المغلف لرؤية خطأ المصرف الذي يظهر لك.
</p>

<h2>
	نقل القيم خارج المغلفات وسمات Fn
</h2>

<p>
	تعرّف الشيفرة البرمجية الموجودة داخل مغلف ما الأمر الذي سيحصل للمراجع أو القيم بعد أن يُقيّم المغلف (بالتالي التعريف على الشيء الذي نُقل <strong>خارج</strong> المغلف إذا كان موجودًا) وذلك بعد أن يحصل المغلف على المرجع أو ملكية قيمة ما من البيئة مكان تعريفه (بالتالي التعريف على الشيء الذي نُقل <strong>إلى</strong> المغلف إذا كان موجودًا). يمكن لمتن المغلف أن يفعل أيًا من الأشياء التالية: نقل قيمة خارج المغلّف، أو التعديل على قيمة داخل المغلف، أو عدم نقل القيمة وعدم تعديلها، أو عدم الحصول على أي قيمة من البيئة في المقام الأول.
</p>

<p>
	تؤثر الطريقة التي يحصل بها المغلف على القيم ويتعامل معها من البيئة على السمات التي يطبقها المغلف والسمات traits هي الطريقة التي تستطيع فيها كل من الدوال <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%87%D9%8A%D8%A7%D9%83%D9%84-structs-%D9%84%D8%AA%D9%86%D8%B8%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1849/" rel="">والهياكل</a> تحديد نوع المغلفات الممكن استخدامها. تطبّق المغلفات تلقائيًا سمةً أو سمتين أو ثلاث من سمات <code>Fn</code> التالية على نحوٍ تراكمي بحسب تعامل متن المغلف للقيم:
</p>

<ol>
	<li>
		السمة <code>FnOnce</code>: تُطبَّق على جميع المغلفات الممكن استدعاؤها مرةً واحدة. تُطبّق جميع المغلفات هذه السمة على الأقل لأنه يمكن لجميع المغلفات أن تُستدعى، وسيطبق المغلف الذي ينقل القيم خارج متنه السمة <code>FnOnce</code> فقط دون أي سمات <code>Fn</code> أخرى لأنه يُمكن استدعاءه مرةً واحدةً فقط.
	</li>
	<li>
		السمة <code>FnMut</code>: تُطبَّق على جميع المغلفات التي لا تنقل القيم خارج متنها، إلا أنها من الممكن أن تعدّل على هذه القيم، ويمكن استدعاء هذه المغلفات أكثر من مرة واحدة.
	</li>
	<li>
		السمة <code>Fn</code>: تُطبَّق على المغلفات التي لا تنقل القيم خارج متنها ولا تعدل على القيم، إضافةً إلى المغلفات التي لا تحصل على أي قيم من البيئة. يمكن لهذه المغلفات أن تُستدعى أكثر من مرة واحدة دون التعديل على بيئتها وهو أمرٌ مهم في حالة استدعاء مغلف عدة مرات على نحوٍ متعاقب.
	</li>
</ol>

<p>
	دعنا ننظر إلى تعريف التابع <code>unwrap_or_else</code> على النوع <code>Option&lt;T&gt;‎</code> الذي استخدمناه في الشيفرة 1:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5937_24" style=""><span class="pln">impl</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> </span><span class="typ">Option</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pub fn unwrap_or_else</span><span class="pun">&lt;</span><span class="pln">F</span><span class="pun">&gt;(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> f</span><span class="pun">:</span><span class="pln"> F</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> T
    </span><span class="kwd">where</span><span class="pln">
        F</span><span class="pun">:</span><span class="pln"> </span><span class="typ">FnOnce</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> T
    </span><span class="pun">{</span><span class="pln">
        match self </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">x</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> x</span><span class="pun">,</span><span class="pln">
            </span><span class="typ">None</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> f</span><span class="pun">(),</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تذكّر أن <code>T</code> هو نوع معمم generic type يمثل نوع القيمة في المتغاير <code>Some</code> للنوع <code>Option</code>، وهذا النوع <code>T</code> يمثل أيضًا نوع القيمة المعادة من الدالة <code>unwrap_or_else</code>، إذ ستحصل الشيفرة البرمجية التي تستدعي <code>unwrap_or_else</code> على النوع <code>Option&lt;String&gt;‎</code> على النوع <code>String</code> على سبيل المثال.
</p>

<p>
	لاحظ تاليًا أن الدالة <code>unwrap_or_else</code> تحتوي على معامل <a href="https://academy.hsoub.com/programming/rust/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D9%85%D8%A9-generic-data-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-rust-r1936/" rel="">نوع معمم</a> إضافي يُدعى <code>F</code> والنوع <code>F</code> هنا هو نوع المعامل ذو الاسم <code>f</code> وهو المغلف الذي نمرره للدالة <code>unwrap_or_else</code> عند استدعائها.
</p>

<p>
	حد السمة trait bound المحدد على النوع المعمم <code>E</code> هو <code>FnOnce() -&gt; T</code>، مما يعني أن النوع <code>F</code> يجب أن يكون قابلًا للاستدعاء مرةً واحدة وألّا يأخذ أي وسطاء وأن يعيد قيمةً من النوع <code>T</code>. يوضح استخدام <code>FnOnce</code> في حد <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%B3%D9%85%D8%A7%D8%AA-traits-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1951/" rel="">السمة</a> القيد: أن <code>unwrap_or_else</code> ستستدعي <code>f</code> مرةً واحدةً على الأكثر. يمكنك من رؤية متن الدالة <code>unwrap_or_else</code> معرفة أن <code>f</code> لن تُستدعى إذا كان المتغاير <code>Some</code> موجودًا في <code>Option</code>، بينما ستُستدعى <code>f</code> مرةً واحدة إذا وُجد المتغاير <code>None</code> في <code>Option</code>. تقبل الدالة <code>unwrap_or_else</code> أنواعًا مختلفة من المغلفات بصورةٍ مرنة، لأن جميع المغلفات تطبّق السمة <code>FnOnce</code>.
</p>

<p>
	<strong>ملاحظة</strong>: يمكن أن تطبق الدوال سمات <code>Fn</code> الثلاث أيضًا. يمكننا استخدام اسم الدالة بدلًا من مغلف عندما نريد شيئًا يطبق واحدةً من سمات <code>Fn</code> إذا كان ما نريد فعله لا يتطلب الحصول على قيمة من البيئة. يمكننا على سبيل المثال، استدعاء <code>unwrap_or_else(Vec::new)‎</code> على قيمة <code>Option&lt;Vec&lt;T&gt;&gt;‎</code> للحصول على شعاع فارغ جديد إذا كانت القيمة هي <code>None</code>.
</p>

<p>
	دعنا ننظر الآن إلى تابع المكتبة القياسية <code>sort_by_key</code> المعرف على الشرائح slices لرؤية الاختلاف بينه وبين <code>unwrap_or_else</code> وسبب استخدام <code>sort_by_key</code> للسمة <code>FnMut</code> بدلًا من السمة <code>FnOnce</code> لحد السمة. يحصل المغلف على وسيط واحد على هيئة مرجع للعنصر الحالي في الشريحة ويُعيد قيمةً من النوع <code>K</code> يمكن ترتيبها، وهذه الدالة مفيدة عندما تريد ترتيب شرحة بسمة attribute معينة لكل عنصر. لدينا في الشيفرة 7 قائمة من نسخ <code>instances</code> من الهيكل <code>Rectangle</code> ونستخدم <code>sort_by_key</code> لترتيبها حسب سمة <code>width</code> من الأصغر إلى الأكبر:
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5937_26" style=""><span class="com">#[derive(Debug)]</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    width</span><span class="pun">:</span><span class="pln"> u32</span><span class="pun">,</span><span class="pln">
    height</span><span class="pun">:</span><span class="pln"> u32</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let mut </span><span class="typ">list</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
        </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln"> height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
        </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
        </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">12</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">];</span><span class="pln">

    </span><span class="typ">list</span><span class="pun">.</span><span class="pln">sort_by_key</span><span class="pun">(|</span><span class="pln">r</span><span class="pun">|</span><span class="pln"> r</span><span class="pun">.</span><span class="pln">width</span><span class="pun">);</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"{:#?}"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">list</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 7: استخدام <code>sort_by_key</code> لترتيب المستطيلات بحسب عرضها
</p>

<p>
	نحصل على الخرج التالي مما سبق:
</p>

<pre class="ipsCode">$ cargo run
   Compiling rectangles v0.1.0 (file:///projects/rectangles)
    Finished dev [unoptimized + debuginfo] target(s) in 0.41s
     Running `target/debug/rectangles`
[
    Rectangle {
        width: 3,
        height: 5,
    },
    Rectangle {
        width: 7,
        height: 12,
    },
    Rectangle {
        width: 10,
        height: 1,
    },
]
</pre>

<p>
	السبب في كون <code>sort_by_key</code> معرفًا ليأخذ مغلفًا يطبق السمة <code>FnMut</code> هو استدعاء الدالة للمغلف عدة مرات: مرةً واحدةً لكل عنصر في الشريحة. لا يحصل المغلف <code>‎|r| r.width</code> على أي قيمة من البيئة أو يعدل عليها أو ينقلها لذا فهو يحقق شروط حد السمة هذه.
</p>

<p>
	توضح الشيفرة 8 مثالًا لمغلف على النقيض، إذ يطبق هذا المغلف السمة <code>FnOnce</code> فقط لأنه ينقل قيمة خارج البيئة، ولن يسمح لنا المصرّف باستخدام هذا المغلف مع <code>sort_by_key</code>:
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5937_28" style=""><span class="com">#[derive(Debug)]</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    width</span><span class="pun">:</span><span class="pln"> u32</span><span class="pun">,</span><span class="pln">
    height</span><span class="pun">:</span><span class="pln"> u32</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let mut </span><span class="typ">list</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
        </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln"> height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
        </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
        </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">12</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">];</span><span class="pln">

    let mut sort_operations </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![];</span><span class="pln">
    let value </span><span class="pun">=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"by key called"</span><span class="pun">);</span><span class="pln">

    </span><span class="typ">list</span><span class="pun">.</span><span class="pln">sort_by_key</span><span class="pun">(|</span><span class="pln">r</span><span class="pun">|</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        sort_operations</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">value</span><span class="pun">);</span><span class="pln">
        r</span><span class="pun">.</span><span class="pln">width
    </span><span class="pun">});</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"{:#?}"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">list</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 8: محاولة استخدام مغلف يطبق السمة <code>FnOnce</code> فقط مع التابع <code>sort_by_key</code>
</p>

<p>
	يمثّل ما سبق طريقةً معقدة (لا تعمل بنجاح) لمحاولة عدّ المرات التي يُستدعى بها التابع <code>sort_by_key</code> عند ترتيب <code>list</code>، وتحاول الشيفرة البرمجية تحقيق ذلك بإضافة <code>value</code> -ألا وهي قيمة من النوع <code>String</code> من بيئة المغلف- إلى الشعاع <code>sort_operations</code>. يحصل المغلف على القيمة <code>value</code>، ثم ينقلها خارج المغلف بنقل ملكيتها إلى الشعاع <code>sort_operations</code>، ويمكن أن يُستدعى هذا المغلف مرةً واحدةً إلا أن محاولة استدعائه للمرة الثانية لن تعمل لأن <code>value</code> لن يكون في البيئة ليُضاف إلى الشعاع <code>sort_operations</code> مجددًا، وبالتالي يطبق هذا المغلف السمة <code>FnOnce</code> فقط، وعندما نحاول تصريف الشيفرة البرمجية السابقة، سنحصل على خطأ مفاده أن <code>value</code> لا يمكن نقلها خارج المغلف لأن المغلف يجب أن يطبّق السمة <code>FnMut</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5937_30" style=""><span class="pln">$ cargo run
   </span><span class="typ">Compiling</span><span class="pln"> rectangles v0</span><span class="pun">.</span><span class="lit">1.0</span><span class="pln"> </span><span class="pun">(</span><span class="pln">file</span><span class="pun">:</span><span class="com">///projects/rectangles)</span><span class="pln">
error</span><span class="pun">[</span><span class="pln">E0507</span><span class="pun">]:</span><span class="pln"> cannot move out of </span><span class="pun">`</span><span class="pln">value</span><span class="pun">`,</span><span class="pln"> a captured variable in an </span><span class="pun">`</span><span class="typ">FnMut</span><span class="pun">`</span><span class="pln"> closure
  </span><span class="pun">--&gt;</span><span class="pln"> src</span><span class="pun">/</span><span class="pln">main</span><span class="pun">.</span><span class="pln">rs</span><span class="pun">:</span><span class="lit">18</span><span class="pun">:</span><span class="lit">30</span><span class="pln">
   </span><span class="pun">|</span><span class="pln">
</span><span class="lit">15</span><span class="pln"> </span><span class="pun">|</span><span class="pln">     let value </span><span class="pun">=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"by key called"</span><span class="pun">);</span><span class="pln">
   </span><span class="pun">|</span><span class="pln">         </span><span class="pun">-----</span><span class="pln"> captured outer variable
</span><span class="lit">16</span><span class="pln"> </span><span class="pun">|</span><span class="pln">
</span><span class="lit">17</span><span class="pln"> </span><span class="pun">|</span><span class="pln">     </span><span class="typ">list</span><span class="pun">.</span><span class="pln">sort_by_key</span><span class="pun">(|</span><span class="pln">r</span><span class="pun">|</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="pun">|</span><span class="pln">                      </span><span class="pun">---</span><span class="pln"> captured by </span><span class="kwd">this</span><span class="pln"> </span><span class="pun">`</span><span class="typ">FnMut</span><span class="pun">`</span><span class="pln"> closure
</span><span class="lit">18</span><span class="pln"> </span><span class="pun">|</span><span class="pln">         sort_operations</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">value</span><span class="pun">);</span><span class="pln">
   </span><span class="pun">|</span><span class="pln">                              </span><span class="pun">^^^^^</span><span class="pln"> move occurs because </span><span class="pun">`</span><span class="pln">value</span><span class="pun">`</span><span class="pln"> has type </span><span class="pun">`</span><span class="typ">String</span><span class="pun">`,</span><span class="pln"> which does not implement the </span><span class="pun">`</span><span class="typ">Copy</span><span class="pun">`</span><span class="pln"> trait

</span><span class="typ">For</span><span class="pln"> more information about </span><span class="kwd">this</span><span class="pln"> error</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">`</span><span class="pln">rustc </span><span class="pun">--</span><span class="pln">explain E0507</span><span class="pun">`.</span><span class="pln">
error</span><span class="pun">:</span><span class="pln"> could not compile </span><span class="pun">`</span><span class="pln">rectangles</span><span class="pun">`</span><span class="pln"> due to previous error</span></pre>

<p>
	يشير الخطأ إلى السطر الذي ننقل فيه القيمة <code>value</code> خارج البيئة داخل متن المغلف، ولتصحيح هذا الخطأ علينا تعديل متن المغلف بحيث لا ينقل القيم خارج البيئة. نحافظ على وجود عدّاد في البيئة ونزيد قيمته داخل المغلف بحيث نستطيع عدّ المرات التي يُستدعى فيها التابع <code>sort_by_key</code>. يعمل المغلف في الشيفرة 9 مع <code>sort_by_key</code> لأنه يحصل فقط على المرجع المتغيّر الخاص بالعداد <code>num_sort_operations</code> ويمكن بالتالي استدعاؤه أكثر من مرة واحدة:
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5937_32" style=""><span class="com">#[derive(Debug)]</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    width</span><span class="pun">:</span><span class="pln"> u32</span><span class="pun">,</span><span class="pln">
    height</span><span class="pun">:</span><span class="pln"> u32</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let mut </span><span class="typ">list</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
        </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln"> height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
        </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
        </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">12</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">];</span><span class="pln">

    let mut num_sort_operations </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">list</span><span class="pun">.</span><span class="pln">sort_by_key</span><span class="pun">(|</span><span class="pln">r</span><span class="pun">|</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        num_sort_operations </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
        r</span><span class="pun">.</span><span class="pln">width
    </span><span class="pun">});</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"{:#?}, sorted in {num_sort_operations} operations"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">list</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 9: استخدام مغلف يطبق السمة <code>FnMut</code> مع التابع <code>sort_by_key</code> دون الحصول على أخطاء
</p>

<p>
	سمات <code>Fn</code> مهمة عند تعريف أو استخدام الدوال أو الأنواع التي تستخدم المغلفات. سنناقش في المقال التالي المكررات iterators، إذ أن العديد من توابع المكررات تأخذ المغلفات مثل وسطاء، لذا تذكّر التفاصيل المتعلقة بالمغلّفات عند قراءة المقالة التالية.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch13-00-functional-features.html" rel="external nofollow">Functional Language Features: Iterators and Closures</a> من كتاب <a href="https://doc.rust-lang.org/stable/book/title-page.html/" rel="external nofollow">The Rust Programming Language</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/rust/%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%B3%D9%84%D8%B3%D9%84%D8%A9-%D9%85%D9%86-%D8%A7%D9%84%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D9%83%D8%B1%D8%B1%D8%A7%D8%AA-iterators-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1993/" rel="">معالجة سلسلة من العناصر باستخدام المكررات iterators في لغة رست</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D9%8A%D8%A6%D8%A9-%D9%88%D8%B7%D8%A8%D8%A7%D8%B9%D8%A9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1979/" rel="">التعامل مع متغيرات البيئة وطباعة الأخطاء في لغة رست</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%AA%D9%86%D8%B8%D9%8A%D9%85-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-tests-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1961/" rel="">تنظيم الاختبارات Tests في لغة رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%B3%D9%85%D8%A7%D8%AA-traits-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1951/" rel="">السمات Traits في لغة رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-%D8%A7%D9%84%D8%A8%D8%AF%D8%A7%D9%8A%D8%A7%D8%AA-r1764/" rel="">تعلم لغة رست Rust: البدايات</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1980</guid><pubDate>Wed, 31 May 2023 16:07:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x645;&#x62A;&#x63A;&#x64A;&#x631;&#x627;&#x62A; &#x627;&#x644;&#x628;&#x64A;&#x626;&#x629; &#x648;&#x637;&#x628;&#x627;&#x639;&#x629; &#x627;&#x644;&#x623;&#x62E;&#x637;&#x627;&#x621; &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A;</title><link>https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D9%8A%D8%A6%D8%A9-%D9%88%D8%B7%D8%A8%D8%A7%D8%B9%D8%A9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1979/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_05/-----.png.e12ce387c326a0e4a98b9c122d0d5f79.png" /></p>
<p>
	بدأنا عملية برمجة أداة سطر الأوامر المشابهة لأداة <code>grep</code> التي تبحث داخل ملف معيّن عن سلسلة نصية محددة في مقال <a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-command-line-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AF%D8%AE%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%B1%D8%AC-r1969/" rel="">التعامل مع الدخل والخرج</a> أين وضع أساس برنامج سطر الأوامر بلغة رست حيث برمجنا منطق التعامل مع الوسطاء المرّرة في سطر الأوامر، ثم حسناه وطورناه أكثر في مقال <a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A5%D8%B9%D8%A7%D8%AF%D8%A9-%D8%A8%D9%86%D8%A7%D8%A1-%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%84%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-modularity-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-r1974/" rel="">إعادة بناء التعليمات البرمجية لتحسين النمطية Modularity والتعامل مع الأخطاء</a> حيث عملنا على تجزئة برنامجنا إلى وحدات منفصلة لتسهل عملية اختبار البرنامج، ثم اختبرنا البرنامج عبر كتابة اختبارات له في <a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-r1978/" rel="">المقال السابق</a><span style="display: none;"> </span>، وسنوضّح أخيرًا في هذا المقال كيفية العمل مع متغيرات البيئة environment variables، إضافةً لكيفية طباعة الأخطاء إلى مجرى الأخطاء القياسي، وهما أمران مهمّان في برامج سطر الأوامر.
</p>

<h2>
	التعامل مع متغيرات البيئة
</h2>

<p>
	سنحسّن على برنامج "minigrep" باستخدام ميزة إضافية، ألا وهي خيار استخدام البحث بنمط عدم حساسية حالة الحروف case-insensitive (سواءٌ كانت أحرف صغيرة أو كبيرة) بحيث يمكن للمستخدم تفعيل هذا النمط أو تعطيله باستخدام متغيرات البيئة. يمكننا جعل هذه الميزة خيارًا <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">لسطر الأوامر</a> إلا أن المستخدم سيكون بحاجةٍ لكتابة هذا الخيار في سطر الأوامر في كل مرة يريد استخدام البرنامج، وباستخدام متغيرات البيئة نجعل ضبط هذا الخيار لمرة واحدة بحيث تكون كل عمليات البحث غير حساسة لحالة الأحرف في جلسة الطرفية تلك.
</p>

<h3>
	كتابة اختبار يفشل لدالة search لميزة عدم حساسية حالة الأحرف
</h3>

<p>
	نُضيف أولًا دالة <code>search_case_insensitive</code> التي تُستدعى عندما يكون لمتغير البيئة قيمةً ما، وسنستمر باتباع عملية التطوير المُقاد بالاختبار هنا، وبالتالي ستكون الخطوة الأولى هي كتابة اختبار يفشل؛ إذ سنضيف اختبارًا جديدًا للدالة الجديدة <code>search_case_insensitive</code> وسنعيد تسمية الاختبار القديم من اسمه السابق <code>one_result</code> إلى <code>case_sensitive</code> لتوضيح الفرق بين الاختبارين كما هو موضح في الشيفرة 20.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1265_12" style=""><span class="com">#[cfg(test)]</span><span class="pln">
mod tests </span><span class="pun">{</span><span class="pln">
    use super</span><span class="pun">::*;</span><span class="pln">

    </span><span class="com">#[test]</span><span class="pln">
    fn case_sensitive</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let query </span><span class="pun">=</span><span class="pln"> </span><span class="str">"duct"</span><span class="pun">;</span><span class="pln">
        let contents </span><span class="pun">=</span><span class="pln"> </span><span class="str">"</span><span class="pln">\
</span><span class="typ">Rust</span><span class="pun">:</span><span class="pln">
safe</span><span class="pun">,</span><span class="pln"> fast</span><span class="pun">,</span><span class="pln"> productive</span><span class="pun">.</span><span class="pln">
</span><span class="typ">Pick</span><span class="pln"> three</span><span class="pun">.</span><span class="pln">
</span><span class="typ">Duct</span><span class="pln"> tape</span><span class="pun">.</span><span class="str">";</span><span class="pln">

        assert_eq</span><span class="pun">!(</span><span class="pln">vec</span><span class="pun">![</span><span class="str">"safe, fast, productive."</span><span class="pun">],</span><span class="pln"> search</span><span class="pun">(</span><span class="pln">query</span><span class="pun">,</span><span class="pln"> contents</span><span class="pun">));</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="com">#[test]</span><span class="pln">
    fn case_insensitive</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let query </span><span class="pun">=</span><span class="pln"> </span><span class="str">"rUsT"</span><span class="pun">;</span><span class="pln">
        let contents </span><span class="pun">=</span><span class="pln"> </span><span class="str">"</span><span class="pln">\
</span><span class="typ">Rust</span><span class="pun">:</span><span class="pln">
safe</span><span class="pun">,</span><span class="pln"> fast</span><span class="pun">,</span><span class="pln"> productive</span><span class="pun">.</span><span class="pln">
</span><span class="typ">Pick</span><span class="pln"> three</span><span class="pun">.</span><span class="pln">
</span><span class="typ">Trust</span><span class="pln"> me</span><span class="pun">.</span><span class="str">";</span><span class="pln">

        assert_eq</span><span class="pun">!(</span><span class="pln">
            vec</span><span class="pun">![</span><span class="str">"Rust:"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Trust me."</span><span class="pun">],</span><span class="pln">
            search_case_insensitive</span><span class="pun">(</span><span class="pln">query</span><span class="pun">,</span><span class="pln"> contents</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 20: إضافة اختبار جديد يفشل لدالة عدم حساسية حالة الحرف التي سنضيفها لاحقًا
</p>

<p>
	لاحظ أننا أضفنا اختبار <code>contents</code> القديم أيضًا، وأضفنا سطرًا جديدًا للنص ".Duct tape" باستخدام حرف D كبير، والذي يجب ألا يطابق استعلام السلسلة النصية "duct" عندما نبحث في حالة حساسية حالة الأحرف. يساعد تغيير الاختبار القديم بهذه الطريقة في التأكد من أننا لن نعطّل خاصية البحث في حالة حساسية الأحرف (وهي الحالة التي طبقناها أولًا، والتي تعمل بنجاح للوقت الحالي). يجب أن ينجح هذا الاختبار الآن ويجب أن يستمر بالنجاح بينما نعمل على خاصية عدم حساسية حالة الأحرف.
</p>

<p>
	يستخدم الاختبار الجديد للبحث بخاصية عدم حساسية حالة الأحرف "rUsT" مثل كلمة بحث، لذلك سنُضيف في الدالة <code>search_case_insensitive</code> الكلمة "rUsT" والتي يجب أن تطابق ":Rust" بحرف R كبير وأن تطابق السطر ".Trust me" أيضًا رغم أن للنتيجتين حالة أحرف مختلفة عن الكلمة التي استخدمناها. هذا هو اختبارنا الذي سيفشل، وستفشل عملية تصريفه لأننا لم نعرّف بعد الدالة <code>search_case_insensitive</code>. ضِف هيكلًا للدالة بحيث تُعيد شعاعًا فارغًا بطريقة مشابهة لما فعلناه في دالة <code>search</code> في الشيفرة 16 حتى نستطيع تصريف الاختبار ورؤية أنه يفشل فعلًا.
</p>

<h3>
	تنفيذ دالة search_case_insensitive
</h3>

<p>
	ستكون الدالة <code>search_case_insensitive</code> الموضحة في الشيفرة 21 مماثلة تقريبًا للدالة <code>search</code>، والفارق الوحيد هنا هو أننا سنحوّل حال الأحرف للكلمة التي نبحث عنها إلى أحرف صغيرة (الوسيط <code>query</code>) إضافةً إلى كل سطر <code>line</code>، بحيث تكون حالة الأحرف متماثلة عند المقارنة بينهما بغضّ النظر عن حالة الأحرف الأصلية.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1265_14" style=""><span class="pln">pub fn search_case_insensitive</span><span class="pun">&lt;</span><span class="str">'</span><span class="pln">a</span><span class="pun">&gt;(</span><span class="pln">
    query</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">str</span><span class="pun">,</span><span class="pln">
    contents</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="str">'</span><span class="pln">a str</span><span class="pun">,</span><span class="pln">
</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;&amp;</span><span class="str">'</span><span class="pln">a str</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let query </span><span class="pun">=</span><span class="pln"> query</span><span class="pun">.</span><span class="pln">to_lowercase</span><span class="pun">();</span><span class="pln">
    let mut results </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">();</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> line in contents</span><span class="pun">.</span><span class="pln">lines</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> line</span><span class="pun">.</span><span class="pln">to_lowercase</span><span class="pun">().</span><span class="pln">contains</span><span class="pun">(&amp;</span><span class="pln">query</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            results</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">line</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    results
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 21: تعريف الدالة <code>search_case_insensitive</code> بحيث تحوّل أحرف الكلمة التي نبحث عنها مع السطر إلى أحرف صغيرة قبل مقارنتهما
</p>

<p>
	نحوّل أولًا أحرف السلسلة النصية <code>query</code> إلى أحرف صغيرة ونخزّنها في متغير يحمل الاسم ذاته، ولتحقيق ذلك نستدعي <code>to_lowercase</code> على السلسلة النصية بحيث تكون النتيجة واحدة بغضّ النظر عن حالة الأحرف المدخلة <code>"rust"</code> أو <code>"RUST"</code> أو <code>"Rust"</code> أو <code>"rUsT"</code> وسنعامل السلسلة النصية المدخلة وكأنها <code>"rust"</code> لإهمال حالة الأحرف، سيعمل التابع <code>to_lowercase</code> على محارف يونيكود Unicode الأساسية إلا أن عمله لن يكون صحيحًا مئة بالمئة. إن كنّا نبرمج تطبيقًا واقعيًا فعلينا أن نقوم بالمزيد من العمل بخصوص هذه النقطة، إلا أننا نناقش في هذا القسم متغيرات البيئة وليس يونيكود، لذا لن نتطرق لذلك الآن.
</p>

<p>
	لاحظ أن <code>query</code> من النوع <code>String</code> الآن وليس شريحة سلسلة نصية لأن استدعاء التابع <code>to_lowercase</code> يُنشئ بيانات جديدة عوضًا عن استخدام مرجع للبيانات الموجودة مسبقًا. لنفرض بأن الكلمة هي <code>"rUsT"</code> كمثال: لا تحتوي شريحة السلسلة النصية على حرف <code>u</code> أو <code>t</code> صغير لنستخدمه لذا علينا حجز مساحة جديدة لنوع <code>String</code> يحتوي على <code>"rust"</code>، وعندما نمرّر <code>query</code> كوسيط إلى التابع <code>contains</code> فنحن بحاجة لإضافة الرمز <code>&amp;</code> لأن شارة <code>contains</code> معرفة بحيث تأخذ شريحة سلسلة نصية.
</p>

<p>
	نضيف من ثمّ استدعاءً للتابع <code>to_lowercase</code> لكل <code>line</code> لتحويل أحرفه إلى أحرف صغيرة، وبذلك نكون حولنا كل من أحرف <code>line</code> و<code>query</code> إلى أحرف صغيرة وسنجد حالات التطابق بغض النظر عن حالة الأحرف في السلسلتين الأصليتين.
</p>

<p>
	دعنا نرى إذا كان تطبيقنا سيجتاز الاختبار:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1265_16" style=""><span class="pln">$ cargo test
   </span><span class="typ">Compiling</span><span class="pln"> minigrep v0</span><span class="pun">.</span><span class="lit">1.0</span><span class="pln"> </span><span class="pun">(</span><span class="pln">file</span><span class="pun">:</span><span class="com">///projects/minigrep)</span><span class="pln">
    </span><span class="typ">Finished</span><span class="pln"> test </span><span class="pun">[</span><span class="pln">unoptimized </span><span class="pun">+</span><span class="pln"> debuginfo</span><span class="pun">]</span><span class="pln"> target</span><span class="pun">(</span><span class="pln">s</span><span class="pun">)</span><span class="pln"> in </span><span class="lit">1.33s</span><span class="pln">
     </span><span class="typ">Running</span><span class="pln"> unittests src</span><span class="pun">/</span><span class="pln">lib</span><span class="pun">.</span><span class="pln">rs </span><span class="pun">(</span><span class="pln">target</span><span class="pun">/</span><span class="pln">debug</span><span class="pun">/</span><span class="pln">deps</span><span class="pun">/</span><span class="pln">minigrep</span><span class="pun">-</span><span class="lit">9cd200e5fac0fc94</span><span class="pun">)</span><span class="pln">

running </span><span class="lit">2</span><span class="pln"> tests
test tests</span><span class="pun">::</span><span class="pln">case_insensitive </span><span class="pun">...</span><span class="pln"> ok
test tests</span><span class="pun">::</span><span class="pln">case_sensitive </span><span class="pun">...</span><span class="pln"> ok

test result</span><span class="pun">:</span><span class="pln"> ok</span><span class="pun">.</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> passed</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> failed</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> ignored</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> measured</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> filtered out</span><span class="pun">;</span><span class="pln"> finished in </span><span class="lit">0.00s</span><span class="pln">

     </span><span class="typ">Running</span><span class="pln"> unittests src</span><span class="pun">/</span><span class="pln">main</span><span class="pun">.</span><span class="pln">rs </span><span class="pun">(</span><span class="pln">target</span><span class="pun">/</span><span class="pln">debug</span><span class="pun">/</span><span class="pln">deps</span><span class="pun">/</span><span class="pln">minigrep</span><span class="pun">-</span><span class="lit">9cd200e5fac0fc94</span><span class="pun">)</span><span class="pln">

running </span><span class="lit">0</span><span class="pln"> tests

test result</span><span class="pun">:</span><span class="pln"> ok</span><span class="pun">.</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> passed</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> failed</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> ignored</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> measured</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> filtered out</span><span class="pun">;</span><span class="pln"> finished in </span><span class="lit">0.00s</span><span class="pln">

   </span><span class="typ">Doc</span><span class="pun">-</span><span class="pln">tests minigrep

running </span><span class="lit">0</span><span class="pln"> tests

test result</span><span class="pun">:</span><span class="pln"> ok</span><span class="pun">.</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> passed</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> failed</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> ignored</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> measured</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> filtered out</span><span class="pun">;</span><span class="pln"> finished in </span><span class="lit">0.00s</span></pre>

<p>
	عظيم، اجتزنا الاختبار. دعنا نستدعي الآن الدالة <code>search_case_insensitive</code> من الدالة <code>run</code>، وسنُضيف أولًا خيار الضبط إلى الهيكل <code>Config</code> للتبديل بين البحث الحساس وغير الحساس لحالة الأحرف، إلا أن إضافة هذا الحقل ستتسبب بأخطاء عند التصريف لأننا لم نسند هذا الحقل إلى أي مكان بعد:
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1265_18" style=""><span class="pln">pub </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Config</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pub query</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
    pub file_path</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
    pub ignore_case</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">bool</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أضفنا الحقل <code>igonre_case</code> الذي يخزن متحول بولياني Boolean، وسنحتاج الدالة <code>run</code> للتتحقق من قيمة <code>ignore_case</code> لتحديد استدعاء أيّ من دالتي البحث: <code>search</code> أو <code>search_case_insensitive</code> كما هو موضح في الشيفرة 22. لن تُصرَّف هذه الشيفرة بنجاح بعد.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1265_20" style=""><span class="pln">pub fn run</span><span class="pun">(</span><span class="pln">config</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Config</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Result</span><span class="pun">&lt;(),</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">Error</span><span class="pun">&gt;&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let contents </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">::</span><span class="pln">read_to_string</span><span class="pun">(</span><span class="pln">config</span><span class="pun">.</span><span class="pln">file_path</span><span class="pun">)?;</span><span class="pln">

    let results </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> config</span><span class="pun">.</span><span class="pln">ignore_case </span><span class="pun">{</span><span class="pln">
        search_case_insensitive</span><span class="pun">(&amp;</span><span class="pln">config</span><span class="pun">.</span><span class="pln">query</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">contents</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        search</span><span class="pun">(&amp;</span><span class="pln">config</span><span class="pun">.</span><span class="pln">query</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">contents</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> line in results </span><span class="pun">{</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"{line}"</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="typ">Ok</span><span class="pun">(())</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 22: استدعاء الدالة <code>search</code> أو الدالة <code>search_case_insensitive</code> بحسب القيمة الموجودة في <code>config.ignore_case</code>
</p>

<p>
	أخيرًا، نحن بحاجة إلى فحص متغير البيئة. الدوال الخاصة بالتعامل مع متغيرات البيئة موجودة في الوحدة module <code>env</code> في المكتبة القياسية، لذا نضيف الوحدة إلى النطاق أعلى الملف src/lib.rs. نستخدم الدالة <code>var</code> من الوحدة <code>env</code> لفحص القيمة المضبوطة في متغير البيئة ذو الاسم <code>IGNORE_CASE</code> كما هو موضح في الشيفرة 23.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1265_22" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">env</span><span class="pun">;</span><span class="pln">
</span><span class="com">// --snip--</span><span class="pln">

impl </span><span class="typ">Config</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pub fn build</span><span class="pun">(</span><span class="pln">args</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;[</span><span class="typ">String</span><span class="pun">])</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Result</span><span class="pun">&lt;</span><span class="typ">Config</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="str">'</span><span class="kwd">static</span><span class="pln"> str</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">len</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">3</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Err</span><span class="pun">(</span><span class="str">"not enough arguments"</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        let query </span><span class="pun">=</span><span class="pln"> args</span><span class="pun">[</span><span class="lit">1</span><span class="pun">].</span><span class="pln">clone</span><span class="pun">();</span><span class="pln">
        let file_path </span><span class="pun">=</span><span class="pln"> args</span><span class="pun">[</span><span class="lit">2</span><span class="pun">].</span><span class="pln">clone</span><span class="pun">();</span><span class="pln">

        let ignore_case </span><span class="pun">=</span><span class="pln"> env</span><span class="pun">::</span><span class="pln">var</span><span class="pun">(</span><span class="str">"IGNORE_CASE"</span><span class="pun">).</span><span class="pln">is_ok</span><span class="pun">();</span><span class="pln">

        </span><span class="typ">Ok</span><span class="pun">(</span><span class="typ">Config</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            query</span><span class="pun">,</span><span class="pln">
            file_path</span><span class="pun">,</span><span class="pln">
            ignore_case</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">})</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 23: التحقق من القيمة المضبوطة في متغير البيئة ذو الاسم <code>IGNORE_CASE</code>
</p>

<p>
	نُنشئ هنا متغيرًا جديدًا يدعى <code>ignore_case</code> ونُسند قيمته باستدعاء الدالة <code>env::var</code> ونمرّر اسم متغير البيئة <code>IGNORE_CASE</code> إليها. تُعيد الدالة <code>env::var</code> قيمةً من النوع <code>Result</code> تحتوي على متغاير variant‏ يدعى <code>Ok</code> يحتوي على قيمة متغير البيئة إذا كان متغير البيئة مضبوطًا إلى قيمة معينة وإلا فهي تعيد قيمة المتغاير <code>Err</code>.
</p>

<p>
	نستخدم التابع <code>is_ok</code> على القيمة <code>Result</code> للتحقق فيما إذا كان متغير البيئة مضبوطًا إلى قيمة معينة أم لا؛ فإذا كان مضبوطًا إلي قيمة فهذا يعني أنه علينا استخدام البحث بتجاهل حالة الأحرف؛ وإذا لم يكن مضبوطًا إلى قيمة معينة، فهذا يعني أن <code>is_ok</code> ستُعيد القيمة <code>false</code> وسينفذ البرنامج الدالة التي تجري البحث الحساس لحالة الأحرف. لا نهتم بقيمة متغير البيئة بل نهتم فقط فيما إذا كانت موجودة أو لا، ولذلك فنحن نستخدم التابع <code>is_ok</code> بدلًا من استخدام <code>unwrap</code> أو <code>expect</code> أو أيًا من التوابع الأخرى التي استخدمناها مع <code>Result</code> سابقًا.
</p>

<p>
	نمرر القيمة في المتغير <code>ignore_case</code> إلى نسخة <code>Config</code> بحيث يمكن للدالة <code>run</code> أن تقرأ هذه القيمة وتقرّر استدعاء الدالة <code>search_case_insensitive</code> أو <code>search</code> كما طبقنا سابقًا في الشيفرة 22.
</p>

<p>
	دعنا نجرّب البرنامج، ولننفذ أولًا البرنامج دون ضبط متغير البيئة وباستخدام الكلمة <code>to</code>، التي يجب أن تمنحنا جميع نتائج المطابقة للكلمة "to" بأحرف صغيرة فقط:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1265_24" style=""><span class="pln">$ cargo run </span><span class="pun">--</span><span class="pln"> to poem</span><span class="pun">.</span><span class="pln">txt
   </span><span class="typ">Compiling</span><span class="pln"> minigrep v0</span><span class="pun">.</span><span class="lit">1.0</span><span class="pln"> </span><span class="pun">(</span><span class="pln">file</span><span class="pun">:</span><span class="com">///projects/minigrep)</span><span class="pln">
    </span><span class="typ">Finished</span><span class="pln"> dev </span><span class="pun">[</span><span class="pln">unoptimized </span><span class="pun">+</span><span class="pln"> debuginfo</span><span class="pun">]</span><span class="pln"> target</span><span class="pun">(</span><span class="pln">s</span><span class="pun">)</span><span class="pln"> in </span><span class="lit">0.0s</span><span class="pln">
     </span><span class="typ">Running</span><span class="pln"> </span><span class="pun">`</span><span class="pln">target</span><span class="pun">/</span><span class="pln">debug</span><span class="pun">/</span><span class="pln">minigrep to poem</span><span class="pun">.</span><span class="pln">txt</span><span class="pun">`</span><span class="pln">
</span><span class="typ">Are</span><span class="pln"> you nobody</span><span class="pun">,</span><span class="pln"> too</span><span class="pun">?</span><span class="pln">
</span><span class="typ">How</span><span class="pln"> dreary to be somebody</span><span class="pun">!</span></pre>

<p>
	يبدو أن البرنامج يعمل بنجاح. دعنا نجرّب الآن تنفيذ البرنامج مع ضبط <code>IGNORE_CASE</code> إلى 1 باستخدام الكلمة ذاتها <code>to</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1265_26" style=""><span class="pln">$ IGNORE_CASE</span><span class="pun">=</span><span class="lit">1</span><span class="pln"> cargo run </span><span class="pun">--</span><span class="pln"> to poem</span><span class="pun">.</span><span class="pln">txt</span></pre>

<p>
	إذا كنت تستخدم PowerShell، فعليك ضبط متغير البيئة، ثم تنفيذ البرنامج على أنهما أمرين منفصلين:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1265_28" style=""><span class="pln">PS</span><span class="pun">&gt;</span><span class="pln"> $Env</span><span class="pun">:</span><span class="pln">IGNORE_CASE</span><span class="pun">=</span><span class="lit">1</span><span class="pun">;</span><span class="pln"> cargo run </span><span class="pun">--</span><span class="pln"> to poem</span><span class="pun">.</span><span class="pln">txt</span></pre>

<p>
	سيجعل ذلك متغير البيئة <code>IGNORE_CASE</code> مستمرًا طوال جلسة الصدفة shell. ويمكن إزالة القيمة عن طريق الأمر <code>Remove-Item</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1265_30" style=""><span class="pln">PS</span><span class="pun">&gt;</span><span class="pln"> </span><span class="typ">Remove</span><span class="pun">-</span><span class="typ">Item</span><span class="pln"> </span><span class="typ">Env</span><span class="pun">:</span><span class="pln">IGNORE_CASE</span></pre>

<p>
	يجب أن نحصل على الأسطر التي تحتوي على الكلمة "to" بغض النظر عن حالة الأحرف:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1265_32" style=""><span class="typ">Are</span><span class="pln"> you nobody</span><span class="pun">,</span><span class="pln"> too</span><span class="pun">?</span><span class="pln">
</span><span class="typ">How</span><span class="pln"> dreary to be somebody</span><span class="pun">!</span><span class="pln">
</span><span class="typ">To</span><span class="pln"> tell your name the livelong day
</span><span class="typ">To</span><span class="pln"> an admiring bog</span><span class="pun">!</span></pre>

<p>
	عظيم، حصلنا على الكلمة "To" ضمن كلمات أخرى. يمكن لبرنامج <code>minigrep</code> الآن البحث عن الكلمات بغض النظر عن حالة الأحرف عن طريق متغير بيئة، ويمكنك الآن التحكم بخيارات البرنامج عن طريق وسطاء سطر الأوامر، أو عن طريق متغيرات البيئة.
</p>

<p>
	تسمح بعض البرامج بوسطاء سطر الأوامر ومتغيرات البيئة في ذات الوقت للخيار نفسه، وفي هذه الحالات يقرّر البرنامج أسبقية أحد الخيارين (وسيط سطر الأوامر أو متغير البيئة). تمرّن بنفسك عن طريق التحكم بحساسية حالة الأحرف عن طريق وسيط سطر أوامر أو متغير بيئة في الوقت ذاته، وحدّد أسبقية أحد الخيارين حسب تفضيلك إذا تعارض الخياران مع بعضهما.
</p>

<p>
	تحتوي الوحدة <code>std::env</code> على العديد من الخصائص الأخرى المفيدة للتعامل مع متغيرات البيئة، اقرأ توثيق الوحدة للاطّلاع على الخيارات المتاحة.
</p>

<h2>
	كتابة رسائل الخطأ إلى مجرى الخطأ القياسي بدلا من مجرى الخرج القياسي
</h2>

<p>
	نكتب رسائل الأخطاء حاليًا إلى الطرفية باستخدام الماكرو <code>println!‎</code>، وفي معظم الطرفيات هناك نوعين من الخرج: خرج قياسي ‏ ‎<code>stdout</code>‎ للمعلومات العامة وخطأ قياسي ‏<code>stderr‎</code> لرسائل الخطأ، ويساعد التمييز بين النوعين المستخدمين بتوجيه خرج نجاح البرنامج إلى ملف مع المحافظة على ظهور رسائل الخطأ على شاشة الطرفية.
</p>

<p>
	الماكرو <code>println!‎</code> قادرٌ فقط على الطباعة إلى الخرج القياسي، لذا علينا استخدام شيء مختلف لطباعة الأخطاء إلى مجرى الأخطاء القياسي standard error stream.
</p>

<h3>
	التحقق من مكان كتابة الأخطاء
</h3>

<p>
	دعنا أولًا نلاحظ كيفية طباعة المحتوى في برنامج <code>minigrep</code> حاليًا إلى الخرج القياسي، متضمنًا ذلك رسائل الأخطاء التي نريد كتابتها إلى مجرى الأخطاء القياسي بدلًا من ذلك، وسنحقّق ذلك بإعادة توجيه مجرى الخرج القياسي إلى ملف والتسبب بخطأ عمدًا، بينما سنُبقي على مجرى الأخطاء القياسي ولن نعيد توجيهه، وبالتالي سيُعرض محتوى مجرى الأخطاء القياسي على الشاشة مباشرةً.
</p>

<p>
	من المتوقع لبرامج سطر الأوامر أن ترسل رسائل الخطأ إلى مجرى الأخطاء القياسي بحيث يمكننا رؤية الأخطاء على الشاشة حتى لو كنّا نعيد توجيه مجرى الخرج القياسي إلى ملف ما. لا يسلك برنامجنا حاليًا سلوكًا جيدًا، إذ أننا على وشك رؤية أن رسائل الخطأ تُخزّن في الملف.
</p>

<p>
	لتوضيح هذا السلوك سننفذ البرنامج باستخدام <code>&lt;</code> ومسار الملف output.txt وهو الملف الذي نريد إعادة توجيه مجرى الخرج القياسية إليه. لن نمرّر أي وسطاء عند التنفيذ، وهو ما سيتسبب بخطأ:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1265_34" style=""><span class="pln">$ cargo run </span><span class="pun">&gt;</span><span class="pln"> output</span><span class="pun">.</span><span class="pln">txt</span></pre>

<p>
	يخبر الرمز <code>&lt;</code> الصدفة بكتابة محتويات الخرج القياسي إلى الملف output.txt بدلًا من الشاشة. لم نحصل على أي رسالة خطأ على الرغم من توقعنا لها بالظهور على الشاشة مما يعني أن الرسالة قد كُتبت إلى الملف. إليك محتوى الملف output.txt:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1265_37" style=""><span class="typ">Problem</span><span class="pln"> parsing arguments</span><span class="pun">:</span><span class="pln"> not enough arguments</span></pre>

<p>
	نعم، تُطبع رسالة الخطأ إلى الخرج القياسي كما توقعنا ومن الأفضل لنا طباعة رسائل الخطأ إلى مجرى الأخطاء القياسي بدلًا من ذلك بحيث يحتوي الملف على البيانات الناتجة عن التنفيذ الناجح، دعنا نحقّق ذلك.
</p>

<h3>
	طباعة الأخطاء إلى مجرى الأخطاء القياسي
</h3>

<p>
	سنستخدم الشيفرة البرمجية في الشيفرة 24 لتعديل طريقة طباعة الأخطاء. لحسن الحظ، الشيفرة البرمجية المتعلقة بطباعة رسائل الخطأ موجودة في دالة واحدة ألا وهي <code>main</code> بفضل عملية إعادة بناء التعليمات البرمجية التي أنجزناها سابقًا. تقدّم لنا المكتبة القياسية الماكرو <code>eprintln!‎</code> الذي يطبع إلى مجرى الأخطاء القياسي، لذا دعنا نعدّل من السطرين الذين نستخدم فيهما الماكرو <code>println!‎</code> ونستخدم <code>eprintln!‎</code> بدلًا من ذلك.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1265_39" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let args</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> env</span><span class="pun">::</span><span class="pln">args</span><span class="pun">().</span><span class="pln">collect</span><span class="pun">();</span><span class="pln">

    let config </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Config</span><span class="pun">::</span><span class="pln">build</span><span class="pun">(&amp;</span><span class="pln">args</span><span class="pun">).</span><span class="pln">unwrap_or_else</span><span class="pun">(|</span><span class="pln">err</span><span class="pun">|</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        eprintln</span><span class="pun">!(</span><span class="str">"Problem parsing arguments: {err}"</span><span class="pun">);</span><span class="pln">
        process</span><span class="pun">::</span><span class="pln">exit</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> let </span><span class="typ">Err</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> minigrep</span><span class="pun">::</span><span class="pln">run</span><span class="pun">(</span><span class="pln">config</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        eprintln</span><span class="pun">!(</span><span class="str">"Application error: {e}"</span><span class="pun">);</span><span class="pln">
        process</span><span class="pun">::</span><span class="pln">exit</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 24: كتابة رسائل الخطأ إلى مجرى الأخطاء القياسية بدلًا من مجرى الخرج القياسي باستخدام <code>eprintln!‎</code>
</p>

<p>
	دعنا ننفذ البرنامج مجددًا بالطريقة ذاتها دون وسطاء وبإعادة توجيه الخرج القياسي إلى ملف باستخدام <code>&lt;</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1265_41" style=""><span class="pln">$ cargo run </span><span class="pun">&gt;</span><span class="pln"> output</span><span class="pun">.</span><span class="pln">txt
</span><span class="typ">Problem</span><span class="pln"> parsing arguments</span><span class="pun">:</span><span class="pln"> not enough arguments</span></pre>

<p>
	نستطيع رؤية الخطأ على الشاشة الآن، ولا يحتوي الملف output.txt أي بيانات وهو السلوك الذي نتوقعه من برامج سطر الأوامر.
</p>

<p>
	دعنا ننفذ البرنامج مجددًا باستخدام الوسطاء لتنفيذ البرنامج دون أخطاء، وبتوجيه الخرج القياسي إلى ملف أيضًا كما يلي:
</p>

<pre class="ipsCode">$ cargo run -- to poem.txt &gt; output.txt
</pre>

<p>
	لن نستطيع رؤية أي خرج على الطرفية، وسيحتوي الملف output.txt على نتائجنا:
</p>

<p>
	اسم الملف: output.txt
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1265_43" style=""><span class="typ">Are</span><span class="pln"> you nobody</span><span class="pun">,</span><span class="pln"> too</span><span class="pun">?</span><span class="pln">
</span><span class="typ">How</span><span class="pln"> dreary to be somebody</span><span class="pun">!</span></pre>

<p>
	يوضح هذا الأمر أننا نستخدم مجرى الخرج القياسي للخرج في حالة النجاح، بينما نستخدم مجرى الأخطاء القياسي في حالة الفشل.
</p>

<h2>
	خاتمة المشروع
</h2>

<p>
	لخّصت جزئية سطر الأوامر من هذه السلسلة بمشروعها الكثير من المفاهيم المهمة التي تعلمناها لحد اللحظة كما أننا تكلمنا عن كيفية إجراء عمليات الدخل والخرج في رست، وذلك باستخدام وسطاء سطر الأوامر والملفات ومتغيرات البيئة والماكرو <code>eprintln!‎</code> لطباعة الأخطاء، ويجب أن تكون الآن مستعدًا لكتابة تطبيقات سطر الأوامر المختلفة. يجب أن تبقى شيفرتك البرمجية منظمةً جيدًا بمساعدة المفاهيم التي تعلمتها في المقالات السابقة وأن تخزن البيانات بفعالية في هياكل بيانات مناسبة وأن تتعامل مع الأخطاء بصورةٍ مناسبة، إضافةً إلى إجراء الاختبارات.
</p>

<p>
	سننظر في المقالات التالية إلى بعض مزايا رست التي تأثرت باللغات الوظيفية، ألا وهي المغلّفات closures والمكررات iterators.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch12-00-an-io-project.html" rel="external nofollow">An I/O Project: Building a Command Line Program</a> من كتاب <a href="https://doc.rust-lang.org/stable/book/title-page.html/" rel="external nofollow">The Rust Programming Language</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%BA%D9%84%D9%81%D8%A7%D8%AA-closures-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1980/" rel="">المغلفات closures في لغة رست Rust</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-r1978/" rel="">كتابة برنامج سطر أوامر بلغة رست: اختبار البرنامج</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">ما هو سطر الأوامر ؟</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-command-line-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AF%D8%AE%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%B1%D8%AC-r1969/" rel="">كتابة برنامج سطر أوامر Command Line بلغة رست Rust: التعامل مع الدخل والخرج</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1979</guid><pubDate>Wed, 24 May 2023 16:09:00 +0000</pubDate></item><item><title>&#x643;&#x62A;&#x627;&#x628;&#x629; &#x628;&#x631;&#x646;&#x627;&#x645;&#x62C; &#x633;&#x637;&#x631; &#x623;&#x648;&#x627;&#x645;&#x631; &#x628;&#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A;: &#x627;&#x62E;&#x62A;&#x628;&#x627;&#x631; &#x627;&#x644;&#x628;&#x631;&#x646;&#x627;&#x645;&#x62C;</title><link>https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-r1978/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_05/-----.png.0f02ad0ebe9b6031082582aec51c2c1c.png" /></p>
<p>
	بدأنا عملية برمجة أداة سطر الأوامر المشابهة لأداة <code>grep</code> التي تبحث داخل ملف معيّن عن سلسلة نصية محددة في مقال <a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-command-line-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AF%D8%AE%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%B1%D8%AC-r1969/" rel="">كتابة برنامج سطر أوامر Command Line بلغة رست Rust: التعامل مع الدخل والخرج</a> وضع أساس برنامج سطر الأوامر بلغة رست حيث برمجنا منطق التعامل مع الوسطاء المرّرة في سطر الأوامر، ثم حسناه وطورناه أكثر في المقال التالي <a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A5%D8%B9%D8%A7%D8%AF%D8%A9-%D8%A8%D9%86%D8%A7%D8%A1-%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%84%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-modularity-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-r1974/" rel="">كتابة برنامج سطر أوامر بلغة رست: إعادة بناء التعليمات البرمجية لتحسين النمطية Modularity والتعامل مع الأخطاء</a> حيث عملنا على تجزئة برنامجنا إلى وحدات منفصلة لتسهل عملية اختبار البرنامج، ونتطرّق في هذا المقال إلى اختبار البرنامج.
</p>

<h2>
	تطوير عمل المكتبة باستخدام التطوير المقاد بالاختبار test-driven
</h2>

<p>
	الآن، وبعد استخراجنا لمعظم منطق البرنامج إلى الملف src/lib.rs، يبقى لدينا منطق الحصول على الوسطاء والتعامل مع الأخطاء في src/main.rs، ومن الأسهل كتابة الاختبارات في هذه الحالة، إذ ستركّز الاختبارات على منطق شيفرتنا البرمجية الأساسية. يمكننا استدعاء الدوال مباشرةً باستخدام مختلف الوسطاء arguments والتحقق من القيمة المعادة دون الحاجة لاستدعاء ملفنا التنفيذي من <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر الأوامر</a>.
</p>

<p>
	نُضيف في هذا القسم منطق البحث إلى البرنامج "minigrep" باستخدام التطوير المُقاد بالاختبار test-driven development -أو اختصارًا TDD- باتباع الخطوات التالية:
</p>

<ol>
	<li>
		كتابة اختبار يفشل وتنفيذه للتأكد من أنه يفشل فعلًا للسبب الذي تتوقعه.
	</li>
	<li>
		كتابة شيفرة برمجية أو التعديل على شيفرة برمجية موجودة مسبقًا لجعل الاختبار الجديد ينجح.
	</li>
	<li>
		إعادة بناء التعليمات البرمجية المُضافة أو المُعدّلة للتأكد من أن الاختبارات ستنجح دومًا.
	</li>
	<li>
		كرّر الأمر مجدّدًا بدءًا من الخطوة 1.
	</li>
</ol>

<p>
	على الرغم من أن التطوير المُقاد بالاختبار هو طريقة من الطرق العديدة الموجودة لكتابة البرمجيات، إلا أنه من الممكن أن يساهم بتصميم الشيفرة البرمجية، إذ تساعد كتابة الاختبارات قبل كتابة الشيفرة البرمجية التي تجعل من الاختبار ناجحًا في المحافظة على اختبار جميع أجزاء الشيفرة البرمجية بسوّية عالية خلال عملية التطوير.
</p>

<p>
	سنختبر تطبيق الخاصية التي ستبحث عن السلسلة النصية المُدخلة في محتويات الملف وتعطينا قائمةً من الأسطر تحتوي على حالات التطابق، ثمّ سنضيف هذه الخاصية في دالة تدعى <code>search</code>.
</p>

<h3>
	كتابة اختبار فاشل
</h3>

<p>
	دعنا نتخلص من تعليمات <code>println!‎</code> من الملفين "src/lib.rs" و "src/main.rs" التي كنا نستخدمها لتفقُّد سلوك البرنامج ولن نحتاج إليها بعد الآن، ثم نضيف الوحدة <code>tests</code> في الملف "src/lib.rs" مع دالة اختبار كما فعلنا في مقال سابق. تحدد دالة الاختبار السلوك الذي نريده من الدالة <code>search</code> ألا وهو: ستأخذ الدالة سلسلةً نصيةً محددةً ونصًا تبحث فيه وستُعيد السطور التي تحتوي على تطابق. توضح الشيفرة 15 هذا الاختبار، إلا أنها لن تُصرَّف بنجاح بعد.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5665_11" style=""><span class="com">#[cfg(test)]</span><span class="pln">
mod tests </span><span class="pun">{</span><span class="pln">
    use super</span><span class="pun">::*;</span><span class="pln">

    </span><span class="com">#[test]</span><span class="pln">
    fn one_result</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let query </span><span class="pun">=</span><span class="pln"> </span><span class="str">"duct"</span><span class="pun">;</span><span class="pln">
        let contents </span><span class="pun">=</span><span class="pln"> </span><span class="str">"</span><span class="pln">\
</span><span class="typ">Rust</span><span class="pun">:</span><span class="pln">
safe</span><span class="pun">,</span><span class="pln"> fast</span><span class="pun">,</span><span class="pln"> productive</span><span class="pun">.</span><span class="pln">
</span><span class="typ">Pick</span><span class="pln"> three</span><span class="pun">.</span><span class="str">";</span><span class="pln">

        assert_eq</span><span class="pun">!(</span><span class="pln">vec</span><span class="pun">![</span><span class="str">"safe, fast, productive."</span><span class="pun">],</span><span class="pln"> search</span><span class="pun">(</span><span class="pln">query</span><span class="pun">,</span><span class="pln"> contents</span><span class="pun">));</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 15: إنشاء اختبار فاشل للدالة <code>search</code> التي كنا نتمنى الحصول عليها
</p>

<p>
	يبحث هذا الاختبار عن السلسلة النصية <code>"duct"</code>، إذ يتألف النص الذي نبحث فيه من ثلاثة أسطر ويحتوي واحدٌ منها فقط السلسلة النصية <code>"duct"</code> (يخبر الخط المائل العكسي backslash بعد علامتي التنصيص المزدوجتين رست بعدم إضافة محرف سطر جديد في بداية محتوى السلسلة النصية المجردة). نتأكد أن القيمة المُعادة من الدالة <code>search</code> تحتوي فقط على السطر الذي نتوقعه.
</p>

<p>
	لا يمكننا تنفيذ هذا الاختبار بعد ورؤيته يفشل لأن الاختبار لا يُصرَّف، والسبب في ذلك هو أن الدالة <code>search</code> غير موجودة بعد. وفقًا لمبادئ التطوير المُقاد بالاختبار، علينا إضافة القليل من الشيفرة البرمجية بحيث يمكننا <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%81%D8%B5%D9%84-%D8%A7%D9%84%D8%A3%D9%88%D9%84-%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%AA%D8%B5%D8%B1%D9%8A%D9%81-compilation-%D9%81%D9%8A-%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r976/" rel="">تصريف</a> الاختبار وتنفيذه بإضافة تعريف الدالة <code>search</code> التي تُعيد <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D9%84%D8%A7%D8%A6%D8%AD%D8%A9-%D9%85%D9%86-%D8%A7%D9%84%D9%82%D9%8A%D9%85-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%A3%D8%B4%D8%B9%D8%A9-vectors-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1875/" rel="">شعاعًا vector</a> فارغًا دومًا كما هو موضح في الشيفرة 16، ومن ثم يجب أن يُصرَّف الاختبار ويفشل لعدم مطابقة الشعاع الفارغ للشعاع الذي يحتوي السطر <code>"safe, fast, productive."</code>.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5665_14" style=""><span class="pln">pub fn search</span><span class="pun">&lt;</span><span class="str">'a&gt;(query: &amp;str, contents: &amp;'</span><span class="pln">a str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;&amp;</span><span class="str">'</span><span class="pln">a str</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    vec</span><span class="pun">![]</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 16: تعريف الدالة <code>search</code> باستخدام شيفرة برمجية قصيرة بحيث يُصرَّف الاختبار
</p>

<p>
	لاحظ أننا بحاجة إلى تعريف دورة حياة lifetime صراحةً تدعى <code>‎'a</code> في بصمة الدالة <code>search</code> واستخدام دورة الحياة في الوسيط <code>contents</code> والقيمة المُعادة. تذكر أننا ذكرنا في مقال سابق أن معاملات دورة الحياة تحدد أي دورات حياة الوسطاء متصلة بدورة حياة القيمة المُعادة، وفي هذه الحالة فإننا نحدد أن الشعاع المُعاد يجب أن يحتوي على <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D8%A8%D8%AA%D8%B1%D9%85%D9%8A%D8%B2-utf-8-%D8%AF%D8%A7%D8%AE%D9%84-%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1876/" rel="">شرائح سلسلة نصية string slices</a> تمثل مرجعًا لشرائح الوسيط <code>contents</code> بدلًا من الوسيط <code>query</code>.
</p>

<p>
	بكلمات أخرى، نخبر رست بأن البيانات المُعادة من الدالة <code>search</code> ستعيش طالما تعيش البيانات المُمرّرة إلى الدالة <code>search</code> في الوسيط <code>contents</code>. يجب أن تكون الشرائح المُستخدمة مثل مراجع للبيانات صالحة حتى يكون المرجع صالحًا، إذ سيكون التحقق من الأمان خاطئًا لو افترض المصرف أننا نُنشئ شرائح سلاسل نصية من <code>query</code> بدلًا من <code>contents</code>.
</p>

<p>
	نحصل على الخطأ التالي إذا نسينا توصيف دورات الحياة وجرّبنا تصريف هذه الدالة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5665_16" style=""><span class="pln">$ cargo build
   </span><span class="typ">Compiling</span><span class="pln"> minigrep v0</span><span class="pun">.</span><span class="lit">1.0</span><span class="pln"> </span><span class="pun">(</span><span class="pln">file</span><span class="pun">:</span><span class="com">///projects/minigrep)</span><span class="pln">
error</span><span class="pun">[</span><span class="pln">E0106</span><span class="pun">]:</span><span class="pln"> missing lifetime specifier
  </span><span class="pun">--&gt;</span><span class="pln"> src</span><span class="pun">/</span><span class="pln">lib</span><span class="pun">.</span><span class="pln">rs</span><span class="pun">:</span><span class="lit">28</span><span class="pun">:</span><span class="lit">51</span><span class="pln">
   </span><span class="pun">|</span><span class="pln">
</span><span class="lit">28</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> pub fn search</span><span class="pun">(</span><span class="pln">query</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">str</span><span class="pun">,</span><span class="pln"> contents</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;&amp;</span><span class="pln">str</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="pun">|</span><span class="pln">                      </span><span class="pun">----</span><span class="pln">            </span><span class="pun">----</span><span class="pln">         </span><span class="pun">^</span><span class="pln"> expected named lifetime parameter
   </span><span class="pun">|</span><span class="pln">
   </span><span class="pun">=</span><span class="pln"> help</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">this</span><span class="pln"> function</span><span class="str">'</span><span class="pln">s </span><span class="kwd">return</span><span class="pln"> type contains a borrowed value</span><span class="pun">,</span><span class="pln"> but the signature does not say whether it is borrowed from </span><span class="pun">`</span><span class="pln">query</span><span class="pun">`</span><span class="pln"> or </span><span class="pun">`</span><span class="pln">contents</span><span class="pun">`</span><span class="pln">
help</span><span class="pun">:</span><span class="pln"> consider introducing a named lifetime parameter
   </span><span class="pun">|</span><span class="pln">
</span><span class="lit">28</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> pub fn search</span><span class="pun">&lt;</span><span class="str">'a&gt;(query: &amp;'</span><span class="pln">a str</span><span class="pun">,</span><span class="pln"> contents</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="str">'a str) -&gt; Vec&lt;&amp;'</span><span class="pln">a str</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="pun">|</span><span class="pln">              </span><span class="pun">++++</span><span class="pln">         </span><span class="pun">++</span><span class="pln">                 </span><span class="pun">++</span><span class="pln">              </span><span class="pun">++</span><span class="pln">

</span><span class="typ">For</span><span class="pln"> more information about </span><span class="kwd">this</span><span class="pln"> error</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">`</span><span class="pln">rustc </span><span class="pun">--</span><span class="pln">explain E0106</span><span class="pun">`.</span><span class="pln">
error</span><span class="pun">:</span><span class="pln"> could not compile </span><span class="pun">`</span><span class="pln">minigrep</span><span class="pun">`</span><span class="pln"> due to previous error</span></pre>

<p>
	لا يمكن لرست معرفة أي الوسيطين نحتاج، لذا يجب أن نصرح عن ذلك مباشرةً. نعلم أن <code>contents</code> هو الوسيط الذي يجب أن يُربط مع القيمة المُعادة باستخدام دورة الحياة وذلك لأنه الوسيط الذي يحتوي على كامل محتوى الملف النصي الذي نريد أن نعيد أجزاءً متطابقةً منه.
</p>

<p>
	لا تتطلب <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغات البرمجة</a> الأخرى ربط الوسطاء للقيمة المعادة في بصمة الدالة، إلا أنك ستعتاد على ذلك مع الممارسة. ننصحك بمقارنة هذا المثال مع مثال موجود في <a href="https://academy.hsoub.com/programming/rust/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D9%85%D8%A9-generic-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1935/" rel="">مقال سابق</a>.
</p>

<p>
	دعنا ننفذ الاختبار:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5665_19" style=""><span class="pln">$ cargo test
   </span><span class="typ">Compiling</span><span class="pln"> minigrep v0</span><span class="pun">.</span><span class="lit">1.0</span><span class="pln"> </span><span class="pun">(</span><span class="pln">file</span><span class="pun">:</span><span class="com">///projects/minigrep)</span><span class="pln">
    </span><span class="typ">Finished</span><span class="pln"> test </span><span class="pun">[</span><span class="pln">unoptimized </span><span class="pun">+</span><span class="pln"> debuginfo</span><span class="pun">]</span><span class="pln"> target</span><span class="pun">(</span><span class="pln">s</span><span class="pun">)</span><span class="pln"> in </span><span class="lit">0.97s</span><span class="pln">
     </span><span class="typ">Running</span><span class="pln"> unittests src</span><span class="pun">/</span><span class="pln">lib</span><span class="pun">.</span><span class="pln">rs </span><span class="pun">(</span><span class="pln">target</span><span class="pun">/</span><span class="pln">debug</span><span class="pun">/</span><span class="pln">deps</span><span class="pun">/</span><span class="pln">minigrep</span><span class="pun">-</span><span class="lit">9cd200e5fac0fc94</span><span class="pun">)</span><span class="pln">

running </span><span class="lit">1</span><span class="pln"> test
test tests</span><span class="pun">::</span><span class="pln">one_result </span><span class="pun">...</span><span class="pln"> FAILED

failures</span><span class="pun">:</span><span class="pln">

</span><span class="pun">----</span><span class="pln"> tests</span><span class="pun">::</span><span class="pln">one_result stdout </span><span class="pun">----</span><span class="pln">
thread </span><span class="str">'main'</span><span class="pln"> panicked at </span><span class="str">'</span><span class="pln">assertion failed</span><span class="pun">:</span><span class="pln"> </span><span class="pun">`(</span><span class="pln">left </span><span class="pun">==</span><span class="pln"> right</span><span class="pun">)`</span><span class="pln">
  left</span><span class="pun">:</span><span class="pln"> </span><span class="pun">`[</span><span class="str">"safe, fast, productive."</span><span class="pun">]`,</span><span class="pln">
 right</span><span class="pun">:</span><span class="pln"> </span><span class="pun">`[]`</span><span class="str">',</span><span class="pln"> src</span><span class="pun">/</span><span class="pln">lib</span><span class="pun">.</span><span class="pln">rs</span><span class="pun">:</span><span class="lit">44</span><span class="pun">:</span><span class="lit">9</span><span class="pln">
note</span><span class="pun">:</span><span class="pln"> run with </span><span class="pun">`</span><span class="pln">RUST_BACKTRACE</span><span class="pun">=</span><span class="lit">1</span><span class="pun">`</span><span class="pln"> environment variable to display a backtrace


failures</span><span class="pun">:</span><span class="pln">
    tests</span><span class="pun">::</span><span class="pln">one_result

test result</span><span class="pun">:</span><span class="pln"> FAILED</span><span class="pun">.</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> passed</span><span class="pun">;</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> failed</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> ignored</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> measured</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> filtered out</span><span class="pun">;</span><span class="pln"> finished in </span><span class="lit">0.00s</span><span class="pln">

error</span><span class="pun">:</span><span class="pln"> test failed</span><span class="pun">,</span><span class="pln"> to rerun pass </span><span class="pun">`--</span><span class="pln">lib</span><span class="pun">`</span></pre>

<p>
	عظيم، فشل الاختبار كما توقعنا. دعنا نجعل الاختبار ينجح الآن.
</p>

<h3>
	كتابة شيفرة برمجية لاجتياز الاختبار
</h3>

<p>
	يفشل اختبارنا حاليًا لأننا نُعيد دائمًا شعاعًا فارغًا، وعلى برنامجنا اتباع الخطوات التالية لتصحيح ذلك وتطبيق <code>search</code>:
</p>

<ul>
	<li>
		المرور على سطور محتوى الملف.
	</li>
	<li>
		التحقق ما إذا كان السطر يحتوي على السلسلة النصية التي نبحث عنها.
	</li>
	<li>
		إذا كان هذا الأمر محققًا: نضيف السطر إلى قائمة القيم التي سنعيدها.
	</li>
	<li>
		إن لم يكن محققًا: لا نفعل أي شيء.
	</li>
	<li>
		نُعيد قائمة الأسطر التي تحتوي على تطابق مع السلسلة النصية التي نبحث عنها.
	</li>
</ul>

<p>
	لنعمل على كل خطوة بالتدريج، بدءًا من المرور على الأسطر على الترتيب.
</p>

<h4>
	المرور على الأسطر باستخدام التابع lines
</h4>

<p>
	توفر لنا رست تابعًا مفيدًا للتعامل مع السلاسل النصية سطرًا تلو الآخر بصورةٍ سهلة وهو تابع <code>lines</code>، ويعمل التابع بالشكل الموضح في الشيفرة 17. انتبه إلى أن الشيفرة 17 لن تُصرَّف بنجاح.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5665_21" style=""><span class="pln">pub fn search</span><span class="pun">&lt;</span><span class="str">'a&gt;(query: &amp;str, contents: &amp;'</span><span class="pln">a str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;&amp;</span><span class="str">'</span><span class="pln">a str</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> line in contents</span><span class="pun">.</span><span class="pln">lines</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// أجرِ بعض العمليات على ‫line</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 17: المرور على أسطر الوسيط <code>contents</code>
</p>

<p>
	يُعيد التابع <code>lines</code> مكرّرًا iterator، وسنتحدث عن المكررات فيما بعد؛ تذّكر أنك رأيت هذه الطريقة باستعمال المكررات في <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A8%D8%B3%D9%8A%D8%B1-%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%B1%D8%A7%D8%B3%D8%AA-rust-r1785/" rel="">الشيفرة 5 من فصل سابق</a> عندما استخدمنا حلقة <code>for</code> مع مكرر لتنفيذ شيفرة برمجية على كل عنصر من عناصر التجميعة collection.
</p>

<h4>
	البحث عن الاستعلام في كل سطر
</h4>

<p>
	الآن نبحث فيما إذا كان السطر يحتوي على السلسلة النصية المحددة بالاستعلام، وتحتوي السلاسل النصية لحسن الحظ على تابع مفيد يدعى <code>contains</code> يفعل هذا نيابةً عنّا. أضف استدعاءً للتابع <code>contains</code> في الدالة <code>search</code> كما هو موضح في الشيفرة 18. لاحظ أن هذه الشيفرة البرمجية لن تُصرَّف بعد.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5665_24" style=""><span class="pln">pub fn search</span><span class="pun">&lt;</span><span class="str">'a&gt;(query: &amp;str, contents: &amp;'</span><span class="pln">a str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;&amp;</span><span class="str">'</span><span class="pln">a str</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> line in contents</span><span class="pun">.</span><span class="pln">lines</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> line</span><span class="pun">.</span><span class="pln">contains</span><span class="pun">(</span><span class="pln">query</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="com">// do something with line</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 18: إضافة ميزة البحث عن السلسلة النصية الموجودة في الوسيط <code>query</code> داخل السطر
</p>

<p>
	بدأنا ببناء وظيفة البرنامج الأساسية الآن، ولتصريف البرنامج بنجاح نحن بحاجة لإعادة قيمة من متن الدالة كما قلنا أننا سنفعل في بصمة الدالة.
</p>

<h4>
	تخزين الأسطر المطابقة
</h4>

<p>
	أخيرًا يجب علينا استخدام طريقة لتخزين الأسطر المُطابقة التي نريد أن نُعيدها من الدالة، ونستخدم لذلك شعاعًا متغيّرًا mutable vector قبل الحلقة <code>for</code> ونستدعي التابع <code>push</code> لتخزين <code>line</code> في الشعاع، ونُعيد هذا الشعاع بعد انتهاء الحلقة <code>for</code> كما هو موضح في الشيفرة 19.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5665_26" style=""><span class="pln">pub fn search</span><span class="pun">&lt;</span><span class="str">'a&gt;(query: &amp;str, contents: &amp;'</span><span class="pln">a str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;&amp;</span><span class="str">'</span><span class="pln">a str</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let mut results </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">();</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> line in contents</span><span class="pun">.</span><span class="pln">lines</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> line</span><span class="pun">.</span><span class="pln">contains</span><span class="pun">(</span><span class="pln">query</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            results</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">line</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    results
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 19: تخزين الأسطر المتطابقة بحيث نستطيع إعادتهم من الدالة
</p>

<p>
	الآن يجب أن تُعيد الدالة <code>search</code> فقط الأسطر التي تحتوي على الوسيط <code>query</code> مما يعني أن اختبارنا سينجح. دعنا ننفّذ الاختبار:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5665_28" style=""><span class="pln">$ cargo test
   </span><span class="typ">Compiling</span><span class="pln"> minigrep v0</span><span class="pun">.</span><span class="lit">1.0</span><span class="pln"> </span><span class="pun">(</span><span class="pln">file</span><span class="pun">:</span><span class="com">///projects/minigrep)</span><span class="pln">
    </span><span class="typ">Finished</span><span class="pln"> test </span><span class="pun">[</span><span class="pln">unoptimized </span><span class="pun">+</span><span class="pln"> debuginfo</span><span class="pun">]</span><span class="pln"> target</span><span class="pun">(</span><span class="pln">s</span><span class="pun">)</span><span class="pln"> in </span><span class="lit">1.22s</span><span class="pln">
     </span><span class="typ">Running</span><span class="pln"> unittests src</span><span class="pun">/</span><span class="pln">lib</span><span class="pun">.</span><span class="pln">rs </span><span class="pun">(</span><span class="pln">target</span><span class="pun">/</span><span class="pln">debug</span><span class="pun">/</span><span class="pln">deps</span><span class="pun">/</span><span class="pln">minigrep</span><span class="pun">-</span><span class="lit">9cd200e5fac0fc94</span><span class="pun">)</span><span class="pln">

running </span><span class="lit">1</span><span class="pln"> test
test tests</span><span class="pun">::</span><span class="pln">one_result </span><span class="pun">...</span><span class="pln"> ok

test result</span><span class="pun">:</span><span class="pln"> ok</span><span class="pun">.</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> passed</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> failed</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> ignored</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> measured</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> filtered out</span><span class="pun">;</span><span class="pln"> finished in </span><span class="lit">0.00s</span><span class="pln">

     </span><span class="typ">Running</span><span class="pln"> unittests src</span><span class="pun">/</span><span class="pln">main</span><span class="pun">.</span><span class="pln">rs </span><span class="pun">(</span><span class="pln">target</span><span class="pun">/</span><span class="pln">debug</span><span class="pun">/</span><span class="pln">deps</span><span class="pun">/</span><span class="pln">minigrep</span><span class="pun">-</span><span class="lit">9cd200e5fac0fc94</span><span class="pun">)</span><span class="pln">

running </span><span class="lit">0</span><span class="pln"> tests

test result</span><span class="pun">:</span><span class="pln"> ok</span><span class="pun">.</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> passed</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> failed</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> ignored</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> measured</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> filtered out</span><span class="pun">;</span><span class="pln"> finished in </span><span class="lit">0.00s</span><span class="pln">

   </span><span class="typ">Doc</span><span class="pun">-</span><span class="pln">tests minigrep

running </span><span class="lit">0</span><span class="pln"> tests

test result</span><span class="pun">:</span><span class="pln"> ok</span><span class="pun">.</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> passed</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> failed</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> ignored</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> measured</span><span class="pun">;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> filtered out</span><span class="pun">;</span><span class="pln"> finished in </span><span class="lit">0.00s</span></pre>

<p>
	نجح الاختبار، لذا فالدالة تعمل.
</p>

<p>
	يجب أن نفكّر بفرص <a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A5%D8%B9%D8%A7%D8%AF%D8%A9-%D8%A8%D9%86%D8%A7%D8%A1-%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%84%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-modularity-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-r1974/" rel="">إعادة بناء التعليمات البرمجية</a> المحتملة بحلول هذه النقطة داخل الدالة <code>search</code> مع المحافظة على نجاح الاختبار للحفاظ على الهدف من الدالة. ليست الشيفرة البرمجية الموجودة في الدالة <code>search</code> سيئة، لكنها لا تستغلّ خصائص المكرّرات المفيدة، وسنعود لهذه الشيفرة البرمجية في مقالات لاحقة عندما نتحدث عن المكررات بتعمق أكبر لمعرفة التحسينات الممكنة.
</p>

<h4>
	استخدام الدالة search في الدالة run
</h4>

<p>
	الآن وبعد عمل الدالة <code>search</code> وتجربتها بنجاح، نحتاج إلى استدعاء <code>search</code> من الدالة <code>run</code>، وتمرير قيمة <code>config.query</code> و <code>contents</code> التي تقرأهما <code>run</code> من الملف إلى الدالة <code>search</code>. تطبع الدالة <code>run</code> كل سطر مُعاد من الدالة <code>search</code>:
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5665_31" style=""><span class="pln">pub fn run</span><span class="pun">(</span><span class="pln">config</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Config</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Result</span><span class="pun">&lt;(),</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">Error</span><span class="pun">&gt;&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let contents </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">::</span><span class="pln">read_to_string</span><span class="pun">(</span><span class="pln">config</span><span class="pun">.</span><span class="pln">file_path</span><span class="pun">)?;</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> line in search</span><span class="pun">(&amp;</span><span class="pln">config</span><span class="pun">.</span><span class="pln">query</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">contents</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"{line}"</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="typ">Ok</span><span class="pun">(())</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ما زلنا نستخدم الحلقة <code>for</code> لإعادة كل سطر من <code>search</code> وطباعته.
</p>

<p>
	يجب أن يعمل كامل البرنامج الآن. دعنا نجرّبه أولًا بكلمة "الضفدع frog" التي ينبغي أن تُعيد سطرًا واحدًا بالتحديد من قصيدة ايميلي ديكنسون Emily Dickinson:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5665_33" style=""><span class="pln">$ cargo run </span><span class="pun">--</span><span class="pln"> frog poem</span><span class="pun">.</span><span class="pln">txt
   </span><span class="typ">Compiling</span><span class="pln"> minigrep v0</span><span class="pun">.</span><span class="lit">1.0</span><span class="pln"> </span><span class="pun">(</span><span class="pln">file</span><span class="pun">:</span><span class="com">///projects/minigrep)</span><span class="pln">
    </span><span class="typ">Finished</span><span class="pln"> dev </span><span class="pun">[</span><span class="pln">unoptimized </span><span class="pun">+</span><span class="pln"> debuginfo</span><span class="pun">]</span><span class="pln"> target</span><span class="pun">(</span><span class="pln">s</span><span class="pun">)</span><span class="pln"> in </span><span class="lit">0.38s</span><span class="pln">
     </span><span class="typ">Running</span><span class="pln"> </span><span class="pun">`</span><span class="pln">target</span><span class="pun">/</span><span class="pln">debug</span><span class="pun">/</span><span class="pln">minigrep frog poem</span><span class="pun">.</span><span class="pln">txt</span><span class="pun">`</span><span class="pln">
</span><span class="typ">How</span><span class="pln"> </span><span class="kwd">public</span><span class="pun">,</span><span class="pln"> like a frog</span></pre>

<p>
	عظيم. دعنا نجرب كلمةً يُفترض أنها موجودة في عدة أسطر مثل "body":
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5665_35" style=""><span class="pln">$ cargo run </span><span class="pun">--</span><span class="pln"> body poem</span><span class="pun">.</span><span class="pln">txt
   </span><span class="typ">Compiling</span><span class="pln"> minigrep v0</span><span class="pun">.</span><span class="lit">1.0</span><span class="pln"> </span><span class="pun">(</span><span class="pln">file</span><span class="pun">:</span><span class="com">///projects/minigrep)</span><span class="pln">
    </span><span class="typ">Finished</span><span class="pln"> dev </span><span class="pun">[</span><span class="pln">unoptimized </span><span class="pun">+</span><span class="pln"> debuginfo</span><span class="pun">]</span><span class="pln"> target</span><span class="pun">(</span><span class="pln">s</span><span class="pun">)</span><span class="pln"> in </span><span class="lit">0.0s</span><span class="pln">
     </span><span class="typ">Running</span><span class="pln"> </span><span class="pun">`</span><span class="pln">target</span><span class="pun">/</span><span class="pln">debug</span><span class="pun">/</span><span class="pln">minigrep body poem</span><span class="pun">.</span><span class="pln">txt</span><span class="pun">`</span><span class="pln">
I</span><span class="str">'</span><span class="pln">m nobody</span><span class="pun">!</span><span class="pln"> </span><span class="typ">Who</span><span class="pln"> are you</span><span class="pun">?</span><span class="pln">
</span><span class="typ">Are</span><span class="pln"> you nobody</span><span class="pun">,</span><span class="pln"> too</span><span class="pun">?</span><span class="pln">
</span><span class="typ">How</span><span class="pln"> dreary to be somebody</span><span class="pun">!</span></pre>

<p>
	وأخيرًا، دعنا نتأكد من أننا لن نحصل على أي سطر عندما تكون الكلمة غير موجودة ضمن أي سطر في القصيدة مثل الكلمة "monomorphization":
</p>

<pre class="ipsCode">$ cargo run -- monomorphization poem.txt
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished dev [unoptimized + debuginfo] target(s) in 0.0s
     Running `target/debug/minigrep monomorphization poem.txt`
</pre>

<p>
	ممتاز، بنينا إصدارنا الخاص المصغّر من أداة البحث الكلاسيكية <code>grep</code>، وتعلمنا الكثير عن هيكلة التطبيقات، كما أننا تعلمنا بعض الأشياء بخصوص دخل وخرج الملفات ودورات الحياة والاختبار والحصول على الوسطاء من سطر الأوامر.
</p>

<p>
	سنوضّح في المقال التالي كيفية العمل مع متغيرات البيئة في ختام هذا المشروع، إضافةً لكيفية طباعة الأخطاء إلى مجرى الأخطاء القياسي، وهما أمران مهمّان في برامج سطر الأوامر.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch12-00-an-io-project.html" rel="external nofollow">An I/O Project: Building a Command Line Program</a> من كتاب <a href="https://doc.rust-lang.org/stable/book/title-page.html/" rel="external nofollow">The Rust Programming Language</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D9%8A%D8%A6%D8%A9-%D9%88%D8%B7%D8%A8%D8%A7%D8%B9%D8%A9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1979/" rel="">التعامل مع متغيرات البيئة وطباعة الأخطاء في لغة رست</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A5%D8%B9%D8%A7%D8%AF%D8%A9-%D8%A8%D9%86%D8%A7%D8%A1-%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%84%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-modularity-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-r1974/" rel="">كتابة برنامج سطر أوامر بلغة رست: إعادة بناء التعليمات البرمجية لتحسين النمطية Modularity والتعامل مع الأخطاء</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">ما هو سطر الأوامر ؟</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-command-line-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AF%D8%AE%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%B1%D8%AC-r1969/" rel="">كتابة برنامج سطر أوامر Command Line بلغة رست Rust: التعامل مع الدخل والخرج</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1978</guid><pubDate>Wed, 17 May 2023 16:05:00 +0000</pubDate></item><item><title>&#x643;&#x62A;&#x627;&#x628;&#x629; &#x628;&#x631;&#x646;&#x627;&#x645;&#x62C; &#x633;&#x637;&#x631; &#x623;&#x648;&#x627;&#x645;&#x631; &#x628;&#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A;: &#x625;&#x639;&#x627;&#x62F;&#x629; &#x628;&#x646;&#x627;&#x621; &#x627;&#x644;&#x62A;&#x639;&#x644;&#x64A;&#x645;&#x627;&#x62A; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A;&#x629; &#x644;&#x62A;&#x62D;&#x633;&#x64A;&#x646; &#x627;&#x644;&#x646;&#x645;&#x637;&#x64A;&#x629; Modularity &#x648;&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x627;&#x644;&#x623;&#x62E;&#x637;&#x627;&#x621;</title><link>https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A5%D8%B9%D8%A7%D8%AF%D8%A9-%D8%A8%D9%86%D8%A7%D8%A1-%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%84%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-modularity-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-r1974/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_05/-------------Modularity---.png.da1b6219ac6d9ca24ba7923eab218dd5.png" /></p>
<p>
	بدأنا في المقال السابق <a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-command-line-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AF%D8%AE%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%B1%D8%AC-r1969/" rel="">كتابة برنامج سطر أوامر Command Line بلغة رست Rust: التعامل مع الدخل والخرج</a> بناء المشروع وسنكمل العملية في هذا المقال حيث سنصلح أربع مشكلات خاصة بهيكل البرنامج وكيفية تعامله مع الأخطاء المحتملة لتحسين برنامجنا. تتمثل المشكلة الأولى في أن للدالة <code>main</code> مهمتان: المرور على لائحة الوسطاء وقراءة الملفات. سيزداد عدد المهام المتفرقة التي تنجزها الدالة <code>main</code> مع نموّ حجم برنامجنا، وتصبح الدالة التي تحتوي على الكثير من المهام صعبة الفهم والاختبار والتعديل دون المخاطرة بتعطيل بعض خصائصها، ومن الأفضل فصل المهام عن بعضها بعضًا بحيث تكون كل دالة مسؤولةً عن مهمةٍ واحدة.
</p>

<p>
	تمتدّ المشكلة إلى مشكلة أخرى ثانية: على الرغم من أن <code>query</code> و <code>file_path</code> تمثلان متغيرات بيئة لبرنامجنا إلا أن متغيرات مثل <code>contents</code> تُستخدم في برنامجنا لتنفيذ المنطق، ومع زيادة حجم الدالة <code>main</code> سيزيد عدد المتغيرات التي سنحتاج إضافتها إلى النطاق مما سيصعّب مهمة متابعة قيمة كل منها، ومن الأفضل تجميع متغيرات الضبط في هيكل واحدة لجعل الهدف منها واضح.
</p>

<p>
	المشكلة الثالثة هي أننا استخدمنا <code>expect</code> لطباعة رسالة خطأ عندما تفشل عملية قراءة الملف، إلا أن رسالة الخطأ تقتصر على طباعة "Should have been able to read the file"، وقد تفشل قراءة ملف ما لعدّة أسباب؛ إذ يمكن أن يكون الملف مفقودًا؛ أو أنك لا تمتلك الأذونات المناسبة لفتحه، وحاليًا فنحن نعرض رسالة الخطأ ذاتها بغض النظر عن سبب الخطأ، وهو أمرٌ لن يمنح المستخدم أي معلومات مفيدة.
</p>

<p>
	رابعًا، استخدمنا <code>expect</code> بصورةٍ متكررة لنتعامل مع الأخطاء المختلفة وإذا نفّذ المستخدم البرنامج دون تحديد عددٍ كافٍ من الوسطاء فسيحصل على الخطأ "index out of bounds" من رست، وهو خطأ لا يشرح بدقة سبب المشكلة. يُفضَّل هنا أن يكون التعامل مع الأخطاء موجودًا في مكان واحد بحيث يعلم المبرمج الذي يعمل على تطوير البرنامج مستقبلًا المكان الذي يجب التوجه إليه في حال أراد تغيير منطق التعامل مع الأخطاء، كما سيسهّل وجود الشيفرة البرمجية التي تتعامل مع الأخطاء في مكان واحد عملية طباعة رسائل خطأ معبّرة للمستخدم.
</p>

<p>
	دعنا نصلح هذه المشاكل الأربع بإعادة بناء التعليمات البرمجية.
</p>

<h2>
	فصل المهام في المشاريع التنفيذية
</h2>

<p>
	مشكلة تنظيم المسؤوليات المختلفة بالنسبة للدالة <code>main</code> هي مشكلة شائعة في الكثير من المشاريع التنفيذية binary projects، ونتيجةً لذلك طوّر مجتمع رست توجيهات عامة لفصل المهام المختلفة الموجودة في البرنامج التنفيذي عندما تصبح الدالة <code>main</code> كبيرة، وتتلخص هذه العملية بالخطوات التالية:
</p>

<ul>
	<li>
		تجزئة برنامجك إلى "main.rs" و "lib.rs" ونقل منطق البرنامج إلى "lib.rs".
	</li>
	<li>
		يمكن أن يبقى منطق الحصول على الوسطاء من سطر الأوامر في "main.rs" طالما هو قصير.
	</li>
	<li>
		عندما يصبح منطق الحصول على الوسطاء من سطر الأوامر معقّدًا صدِّره من "main.rs" إلى "lib.rs".
	</li>
</ul>

<p>
	يجب أن تكون المهام التي يجب أن تبقى في <code>main</code> بعد هذه العملية محصورةً بما يلي:
</p>

<ul>
	<li>
		استدعاء منطق الحصول على وسطاء سطر الأوامر باستخدام قيم الوسطاء.
	</li>
	<li>
		إعداد أي ضبط لازم.
	</li>
	<li>
		استدعاء الدالة <code>run</code> في "lib.rs".
	</li>
	<li>
		التعامل مع الخطأ إذا أعادت الدالة <code>run</code> خطأ.
	</li>
</ul>

<p>
	يحلّ هذا النمط كل ما يتعلق بفصل المهام، إذ يتعامل "main.rs" بكل شيء يخص تشغيل البرنامج، بينما يتعامل "lib.rs" مع منطق المهة المطروحة. بما أنك لا تستطيع اختبار الدالة <code>main</code> مباشرةً، سيساعدك هذا الهيكل في اختبار كل منطق برنامجك بنقله إلى دوال موجودة في "lib.rs"، وستكون الشيفرة البرمجية المتبقىة في "main.rs" قصيرة وسيكون التحقق من صحتها بالنظر إليها ببساطة كافيًا. دعنا نعيد كتابة التعليمات البرمجية باستخدام هذه الخطوات.
</p>

<h2>
	استخلاص الشيفرة البرمجية التي تحصل على الوسطاء
</h2>

<p>
	سنستخرج خاصية الحصول على الوسطاء إلى دالة تستدعيها <code>main</code> لتحضير نقل منطق سطر الأوامر إلى src/lib.rs. توضح الشيفرة 5 بدايةً جديدةً من الدالة <code>main</code> تستدعي دالةً جديدة تدعى <code>parse_config</code> وسنعرّفها حاليًا في src/main.rs.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9742_8" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let args</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> env</span><span class="pun">::</span><span class="pln">args</span><span class="pun">().</span><span class="pln">collect</span><span class="pun">();</span><span class="pln">

    let </span><span class="pun">(</span><span class="pln">query</span><span class="pun">,</span><span class="pln"> file_path</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> parse_config</span><span class="pun">(&amp;</span><span class="pln">args</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// --snip--</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

fn parse_config</span><span class="pun">(</span><span class="pln">args</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;[</span><span class="typ">String</span><span class="pun">])</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">(&amp;</span><span class="pln">str</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let query </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">args</span><span class="pun">[</span><span class="lit">1</span><span class="pun">];</span><span class="pln">
    let file_path </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">args</span><span class="pun">[</span><span class="lit">2</span><span class="pun">];</span><span class="pln">

    </span><span class="pun">(</span><span class="pln">query</span><span class="pun">,</span><span class="pln"> file_path</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	[الشيفرة 5: استخراج الدالة <code>parse_config</code> من <code>main</code>]
</p>

<p>
	ما زلنا نجمع وسطاء سطر الأوامر في شعاع، إلا أننا نمرّر الشعاع كاملًا إلى الدالة <code>parse_config</code> بدلًا من إسناد القيمة في الدليل "1" إلى المتغير <code>query</code> والقيمة في الدليل "2" إلى المتغير <code>file_path</code> داخل الدالة <code>main</code>، إذ تحتوي الدالة <code>parse_config</code> على المنطق الذي يحدّد أي القيمتين سيُخزَّن في أي المتغيّرين ومن ثم تعديل القيم إلى الدالة <code>main</code>، إلا أننا ما زلنا نُنشئ المتغيرين <code>query</code> و <code>file_path</code> في <code>main</code>، لكن لا تحمل <code>main</code> مسؤولية تحديد أي وسطاء سطر الأوامر تنتمي إلى أي المتغيرات.
</p>

<p>
	قد تبدو هذه الإضافة مبالغةً شديدةً في برنامجنا البسيط هذا، إلا أننا نعيد بناء التعليمات البرمجية بخطوات صغيرة وتدريجية. نفّذ هذا البرنامج بعد تطبيق التعديلات لتتأكد من أن الحصول على الوسطاء ما زال يعمل. من المحبّذ التحقق من تنفيذ البرنامج بعد كل تعديل بحيث تستطيع معرفة سبب المشكلة فورًا إذا حصلت.
</p>

<h3>
	تجميع قيم الضبط
</h3>

<p>
	يمكننا اتخاذ خطوة بسيطة لتحسين الدالة <code>parse_config</code> أكثر، إذ أننا نعيد حاليًا صف tuple على الرغم من أننا نجزّء هذا الصف مباشرةً إلى أجزاء متفرقة من جديد، وهذا يشير إلى أننا لا نطبّق الفكرة صحيحًا.
</p>

<p>
	كون <code>config</code> جزءًا من <code>parse_config</code> هو مؤشر آخر لوجود احتمالية تحسين، إذ يشير ذلك أن القيمتين التي نُعيدهما مترابطتان وهما جزء من قيمة ضبط واحدة، إلا أننا لا ننقل هذا المعنى بهيكل البيانات، كما أننا نجمع القيمتين في صف. دعنا نضع القيمتين بدلًا من ذلك في هيكل واحد ونمنح كلًا من حقول الهيكل اسمًا معبرًا. ستكون الشيفرة البرمجية بعد ذلك أسهل فهمًا للمطورين الذين سيعملون على الشيفرة البرمجية مستقبلًا، إذ سيوضّح ذلك كيف ترتبط القيم المختلفة مع بعضها بعضًا وهدف كل واحدة منها.
</p>

<p>
	توضح الشيفرة 6 التحسينات التي أجريناها على الدالة <code>parse_config</code>.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9742_10" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let args</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> env</span><span class="pun">::</span><span class="pln">args</span><span class="pun">().</span><span class="pln">collect</span><span class="pun">();</span><span class="pln">

    let config </span><span class="pun">=</span><span class="pln"> parse_config</span><span class="pun">(&amp;</span><span class="pln">args</span><span class="pun">);</span><span class="pln">

    println</span><span class="pun">!(</span><span class="str">"Searching for {}"</span><span class="pun">,</span><span class="pln"> config</span><span class="pun">.</span><span class="pln">query</span><span class="pun">);</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"In file {}"</span><span class="pun">,</span><span class="pln"> config</span><span class="pun">.</span><span class="pln">file_path</span><span class="pun">);</span><span class="pln">

    let contents </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">::</span><span class="pln">read_to_string</span><span class="pun">(</span><span class="pln">config</span><span class="pun">.</span><span class="pln">file_path</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">expect</span><span class="pun">(</span><span class="str">"Should have been able to read the file"</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// --snip--</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Config</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    query</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
    file_path</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

fn parse_config</span><span class="pun">(</span><span class="pln">args</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;[</span><span class="typ">String</span><span class="pun">])</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Config</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let query </span><span class="pun">=</span><span class="pln"> args</span><span class="pun">[</span><span class="lit">1</span><span class="pun">].</span><span class="pln">clone</span><span class="pun">();</span><span class="pln">
    let file_path </span><span class="pun">=</span><span class="pln"> args</span><span class="pun">[</span><span class="lit">2</span><span class="pun">].</span><span class="pln">clone</span><span class="pun">();</span><span class="pln">

    </span><span class="typ">Config</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> query</span><span class="pun">,</span><span class="pln"> file_path </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	[الشيفرة 6: إعادة بناء التعليمات البرمجية في الدالة <code>parse_config</code> لإعادة نسخة instance من الهيكل <code>Config</code>]
</p>

<p>
	أَضفنا هيكلًا يدعى <code>Config</code> وعرّفناه، بحيث يحتوي على حقلين <code>query</code> و <code>file_path</code>. تشير بصمة signature الدالة <code>parse_config</code> الآن أنها تُعيد قيمةً من النوع <code>Config</code>، إلا أننا اعتدنا إعادة شرائح السلسلة النصية string slice التي تمثل مرجعًا للنوع <code>String</code> في <code>args</code>، لذلك نعرّف <code>Config</code> بحيث يحتوي على قيمتين مملوكتين owned من النوع <code>String</code>. المتغير <code>args</code> في <code>main</code> هو المالك لقيم الوسطاء ويسمح للدالة <code>parse_config</code> باستعارتها فقط، مما يعني أننا سنخرق قواعد الاستعارة في رست إذا حاول <code>Config</code> أخذ ملكية القيم من <code>args</code>.
</p>

<p>
	هناك عدة طرق نستطيع فيها إدارة بيانات <code>String</code>، إلا أن أسهل الطرق غير فعال وهو باستدعاء التابع <code>clone</code> على القيم، مما سيعطينا نسخةً من البيانات بحيث تستطيع <code>Config</code> امتلاكها وهو أمرٌ يستغرق وقتًا ويشغل ذاكرةً أكبر مقارنةً باستخدام مرجع لبيانات السلسلة النصية، إلا أن نسخ البيانات يجعل من شيفرتنا البرمجية واضحةً لأنه ليس علينا وقتها إدارة دورات حياة المراجع، وفي هذه الحالة تُعد مقايضة الفعالية بالأداء بصورةٍ طفيفة مقابل البساطة أمرًا مقبولًا.
</p>

<p>
	حدّثنا <code>main</code> بحيث تضع نسخة من الهيكل <code>Config</code> مُعادة بواسطة الدالة <code>parse_config</code> إلى متغير يدعى <code>config</code> وحدّثنا الشيفرة البرمجية التي استخدمت سابقًا المتغيرات <code>query</code> و <code>file_path</code> بصورةٍ منفصلة، إذ نستخدم الآن الحقول الموجودة في <code>Config</code> بدلًا من ذلك.
</p>

<p>
	أصبحت شيفرتنا البرمجية الآن توضح بصورةٍ أفضل أن القيمتين <code>query</code> و <code>file_path</code> مترابطتان وأن الهدف منهما هو ضبط كيفية عمل البرنامج. أي شيفرة برمجية تستخدم هاتين القيمتين ستعثر عليهما في نسخة <code>config</code> في الحقول المسمّاة بحسب الهدف منهما.
</p>

<p>
	<strong>ملاحظة حول سلبيات استخدام clone:</strong> يميل مبرمجو لغة رست لتفادي استخدام <code>clone</code> لتصحيح مشاكل الملكية بسبب الوقت الذي يستغرقه تنفيذها. ستتعلم لاحقًا كيفية استخدام توابع ذات كفاءة في حالات مشابهة لهذه، إلا أن نسخ بعض السلاسل النصية حاليًا أمرٌ مقبول لأنك تنسخ هذه القيم مرةً واحدةً فقط ومسار الملف والكلمة التي تبحث عنها ليستا بالحجم الكبير. من الأفضل وجود برنامج لا يعمل بفعالية مئةً بالمئة من محاولة زيادة فعالية الشيفرة البرمجية بصورةٍ مفرطة في محاولتك الأولى، إذ سيصبح الأمر أسهل بالنسبة لك مع اكتسابك للخبرة في رست، بحيث تستطيع كتابة حلول برمجية أكثر فاعلية ومن المقبول الآن استدعاء <code>clone</code>.
</p>

<h2>
	إنشاء باني للهيكل Config
</h2>

<p>
	استخلصنا بحلول هذه اللحظة المنطق المسؤول عن الحصول على قيم وسطاء سطر الأوامر من الدالة <code>main</code> ووضعناه في الدالة <code>parse_config</code>، وساعدنا هذا في رؤية كيفية ارتباط القيمتين <code>query</code> و <code>file_path</code> ببعضهما، ثم أضفنا هيكل <code>Config</code> لتسمية الهدف من القيمتين <code>query</code> و <code>file_path</code> ولنكون قادرين على إعادة أسماء القيم مثل حقول هيكل من الدالة <code>parse_config</code>.
</p>

<p>
	إذًا، أصبح الآن الهدف من الدالة <code>parse_config</code> إنشاء نسخ من الهيكل <code>Config</code>، ويمكننا تعديل <code>parse_config</code> من دالة اعتيادية إلى دالة تدعى <code>new</code> مرتبطة بالهيكل <code>Config</code>، وسيؤدي هذا التعديل إلى جعل شيفرتنا البرمجية رسميةً idiomatic أكثر.
</p>

<p>
	يمكننا إنشاء نسخ من <a href="https://academy.hsoub.com/programming/rust/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D9%85%D8%A9-generic-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1935/" rel="">الأنواع الموجودة في المكتبة القياسية</a> مثل <code>String</code> باستدعاء <code>String::new</code>، وكذلك يمكننا بتعديل الدالة <code>parse_config</code> إلى دالة <code>new</code> مرتبطة مع الهيكل <code>Config</code> استدعاء <code>Config::new</code> للحصول على نسخ من <code>Config</code>. توضح الشيفرة 7 التعديلات التي نحتاج لإجرائها.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9742_12" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let args</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> env</span><span class="pun">::</span><span class="pln">args</span><span class="pun">().</span><span class="pln">collect</span><span class="pun">();</span><span class="pln">

    let config </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Config</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(&amp;</span><span class="pln">args</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// --snip--</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// --snip--</span><span class="pln">

impl </span><span class="typ">Config</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn </span><span class="kwd">new</span><span class="pun">(</span><span class="pln">args</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;[</span><span class="typ">String</span><span class="pun">])</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Config</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let query </span><span class="pun">=</span><span class="pln"> args</span><span class="pun">[</span><span class="lit">1</span><span class="pun">].</span><span class="pln">clone</span><span class="pun">();</span><span class="pln">
        let file_path </span><span class="pun">=</span><span class="pln"> args</span><span class="pun">[</span><span class="lit">2</span><span class="pun">].</span><span class="pln">clone</span><span class="pun">();</span><span class="pln">

        </span><span class="typ">Config</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> query</span><span class="pun">,</span><span class="pln"> file_path </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	[الشيفرة 7: تغيير الدالة <code>parse_config</code> إلى <code>Config::new</code>]
</p>

<p>
	حدّثنا الدالة <code>main</code> التي استدعينا فيها <code>parse_config</code> سابقًا لتستدعي <code>Config::new</code> بدلًا من ذلك، وعدّلنا اسم الدالة <code>parse_config</code> إلى <code>new</code> ونقلناها لتصبح داخل كتلة <code>impl</code>، مما يربط الدالة <code>new</code> مع الهيكل <code>Config</code>. جرّب تصريف الشيفرة البرمجية مجددًا لتتأكد من أنها تعمل دون مشاكل.
</p>

<h2>
	تحسين التعامل مع الأخطاء
</h2>

<p>
	سنعمل الآن على تصحيح التعامل مع الأخطاء. تذكّر أن محاولتنا للوصول إلى القيم الموجودة في الشعاع <code>args</code> في الدليل 1 أو الدليل 2 تسببت بهلع البرنامج في حال احتواء <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D9%84%D8%A7%D8%A6%D8%AD%D8%A9-%D9%85%D9%86-%D8%A7%D9%84%D9%82%D9%8A%D9%85-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%A3%D8%B4%D8%B9%D8%A9-vectors-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1875/" rel="">الشعاع</a> أقل من 3 عناصر. جرّب تنفيذ البرنامج دون أي وسطاء، من المفترض أن تحصل على رسالة الخطأ التالية:
</p>

<pre class="ipsCode">$ cargo run
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished dev [unoptimized + debuginfo] target(s) in 0.0s
     Running `target/debug/minigrep`
thread 'main' panicked at 'index out of bounds: the len is 1 but the index is 1', src/main.rs:27:21
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
</pre>

<p>
	يمثل السطر <code>index out of bounds: the len is 1 but the index is 1</code> رسالة خطأ موجهة للمبرمجين، ولن تساعد مستخدم البرنامج لفهم الخطأ. دعنا نصلح ذلك الآن.
</p>

<h3>
	تحسين رسالة الخطأ
</h3>

<p>
	سنُضيف في الشيفرة 8 اختبارًا في الدالة <code>new</code> يتأكد من أن الشريحة طويلة كفاية قبل محاولة الوصول إلى الدليل 1 و2، وإذا لم كانت الشريحة طويلة كفاية، <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1920/" rel="">سيهلع البرنامج</a> وسيعرض رسالة خطأ أفضل من الرسالة التي رأيناها سابقًا.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9742_15" style=""><span class="pln">    </span><span class="com">// --snip--</span><span class="pln">
    fn </span><span class="kwd">new</span><span class="pun">(</span><span class="pln">args</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;[</span><span class="typ">String</span><span class="pun">])</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Config</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">len</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">3</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            panic</span><span class="pun">!(</span><span class="str">"not enough arguments"</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="com">// --snip--</span></pre>

<p>
	[الشيفرة 8: إضافة اختبار للتحقق من عدد الوسطاء]
</p>

<p>
	الشيفرة البرمجية مشابهة لما فعلناه في مقال سابق عند كتابة الدالة <code>Guess::new</code>، إذ استدعينا الماكرو <code>panic!‎</code> عندما كان الوسيط <code>value</code> خارج مجال القيم الصالحة، إلا أننا نفحص طول <code>args</code> إذا كان على الأقل بطول 3 بدلًا من فحص مجال القيم، وتتابع بقية الدالة فيما بعد عملها بافتراض أن الشرط محقق. إذا احتوى الشعاع <code>args</code> على أقل من ثلاثة عناصر، سيتحقق الشرط الذي يستدعي الماكرو <code>panic!‎</code> وبالتالي سيتوقف البرنامج مباشرةً.
</p>

<p>
	دعنا نجرّب تنفيذ البرنامج بعد إضافتنا للسطور البرمجية القليلة هذه في دالة <code>new</code> دون أي وسطاء ونرى رسالة الخطأ:
</p>

<pre class="ipsCode">$ cargo run
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished dev [unoptimized + debuginfo] target(s) in 0.0s
     Running `target/debug/minigrep`
thread 'main' panicked at 'not enough arguments', src/main.rs:26:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
</pre>

<p>
	الخرج هذا أفضل لأنه يدلّنا على الخطأ بوضوح أكبر، إلا أننا نحصل على معلومات زائدة لسنا بحاجة لعرضها للمستخدم في ذات الوقت. لعلّ هذه الطريقة ليست بالطريقة المثلى؛ إذ أن استدعاء <code>panic!‎</code> ملائم لعرض الخطأ في مرحلة كتابة الشيفرة البرمجية للمبرمج وليس في مرحلة استخدام البرنامج للمستخدم (كما ناقشنا في مقالات سابقة)، نستخدم بدلًا من ذلك طريقة أخرى تعلمناها سابقًا ألا وهي إعادة النوع <code>Result</code> الذي يمثّل قيمة نجاح أو فشل.
</p>

<h3>
	إعادة النوع Result بدلا من استدعاء panic!‎
</h3>

<p>
	يمكننا إعادة قيمة <code>Result</code> التي تحتوي على نسخة من <code>Config</code> في حال النجاح وقيمة تصف الخطأ في حالة الفشل. سنغيّر أيضًا اسم الدالة <code>new</code> إلى <code>build</code>، إذ سيتوقع العديد من المبرمجين أن الدالة <code>new</code> لن تفشل أبدًا. يمكننا استخدام النوع <code>Result</code> للإشارة إلى مشكلة عندما تتواصل الدالة <code>Config::build</code> مع الدالة <code>main</code>، ومن ثم يمكننا التعديل على <code>main</code> بحيث تحوّل المتغاير‏ <code>Err</code> إلى خطأ أوضح للمستخدم دون النص الذي يتضمن <code>thread 'main'‎</code> و <code>RUST_BACKTRACE</code> وهو ما ينتج عن استدعاء <code>panic!‎</code>.
</p>

<p>
	توضح الشيفرة 9 التغييرات التي أجريناها لقيمة الدالة المُعادة -التي تحمل الاسم <code>Config::build</code> الآن- ومتن الدالة الذي يُعيد قيمة <code>Result</code>. لاحظ أن هذه الشيفرة لن تُصرَّف حتى نعدل الدالة <code>main</code> أيضًا، وهو ما سنفعله في الشيفرة التي تليها.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9742_18" style=""><span class="pln">impl </span><span class="typ">Config</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn build</span><span class="pun">(</span><span class="pln">args</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;[</span><span class="typ">String</span><span class="pun">])</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Result</span><span class="pun">&lt;</span><span class="typ">Config</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="str">'</span><span class="kwd">static</span><span class="pln"> str</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">len</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">3</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Err</span><span class="pun">(</span><span class="str">"not enough arguments"</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        let query </span><span class="pun">=</span><span class="pln"> args</span><span class="pun">[</span><span class="lit">1</span><span class="pun">].</span><span class="pln">clone</span><span class="pun">();</span><span class="pln">
        let file_path </span><span class="pun">=</span><span class="pln"> args</span><span class="pun">[</span><span class="lit">2</span><span class="pun">].</span><span class="pln">clone</span><span class="pun">();</span><span class="pln">

        </span><span class="typ">Ok</span><span class="pun">(</span><span class="typ">Config</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> query</span><span class="pun">,</span><span class="pln"> file_path </span><span class="pun">})</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	[الشيفرة 9: إعادة قيمة <code>Result</code> من الدالة <code>Config::build</code>]
</p>

<p>
	تُعيد الدالة <code>build</code> قيمة <code>Result</code> بنسخة <code>Config</code> في حال النجاح و <code>‎&amp;'static str</code> في حال الفشل، وستكون قيم الأخطاء سلاسل نصية مجرّدة string literals دومًا بدورة حياة <code>‎'static</code>.
</p>

<p>
	أجرينا تعديلين على محتوى الدالة، فبدلًا من استدعاء <code>panic!‎</code> عندما لا يمرّر المستخدم عددًا كافيًا من الوسطاء، أصبحنا نُعيد قيمة <code>Err</code>، وغلّفنا قيمة <code>Config</code> المُعادة بمتغاير <code>Ok</code>. تجعل هذه التغييرات من الدالة متوافقة مع نوع بصمتها الجديد.
</p>

<p>
	تسمح إعادة القيمة <code>Err</code> من <code>Config::build</code> إلى الدالة <code>main</code> بالتعامل مع القيمة <code>Result</code> المُعادة من الدالة <code>build</code> ومغادرة البرنامج بصورةٍ أفضل في حالة الفشل.
</p>

<h3>
	استدعاء الدالة Config::build والتعامل مع الأخطاء
</h3>

<p>
	نحتاج لتعديل الدالة <code>main</code> بحيث تتعامل مع النوع <code>Result</code> المُعاد إليها من الدالة <code>Config::build</code> كي نتعامل مع الأخطاء عند حدوثها وطباعة رسالة مفهومة للمستخدم، وهذا التعديل موضّح في الشيفرة 10. كما أننا سنعدّل البرنامج بحيث نخرج من أداة سطر الأوامر برمز خطأ غير صفري عند استدعاء <code>panic!‎</code> إلا أننا سنطبق ذلك يدويًا. رمز الخطأ غير الصفري nonzero exit code هو اصطلاح للإشارة إلى العملية التي استدعت البرنامج الذي تسبب بالخروج من برنامجنا برمز الخطأ.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9742_20" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">process</span><span class="pun">;</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let args</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> env</span><span class="pun">::</span><span class="pln">args</span><span class="pun">().</span><span class="pln">collect</span><span class="pun">();</span><span class="pln">

    let config </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Config</span><span class="pun">::</span><span class="pln">build</span><span class="pun">(&amp;</span><span class="pln">args</span><span class="pun">).</span><span class="pln">unwrap_or_else</span><span class="pun">(|</span><span class="pln">err</span><span class="pun">|</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"Problem parsing arguments: {err}"</span><span class="pun">);</span><span class="pln">
        process</span><span class="pun">::</span><span class="pln">exit</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    </span><span class="com">// --snip--</span></pre>

<p>
	[الشيفرة 10: الخروج برمز خطأ إذا فشل بناء <code>Config</code>]
</p>

<p>
	استخدمنا في الشيفرة السابقة تابعًا لم نشرحه بالتفصيل بعد، ألا وهو <code>unwrap_or_else</code> وهو تابع معرَّف في <code>Result&lt;T, E&gt;‎</code> في المكتبة القياسية. يسمح لنا استخدام التابع <code>unwrap_or_else</code> بتعريف بعض طرق التعامل مع الأخطاء المخصصة التي لا تستخدم الماكرو <code>panic!‎</code>. سلوك التابع مماثل للتابع <code>unwrap</code> إذا كانت قيمة <code>Result</code> هي <code>Ok</code>، إذ أنه يعيد القيمة المغلّفة داخل <code>Ok</code>، إلا أن التابع يستدعي شيفرةً برمجيةً في مغلِّفه closure إذا كانت القيمة <code>Err</code> وهي دالة مجهولة anonymous function نعرّفها ونمرّرها بمثابة وسيط للتابع <code>unwrap_or_else</code>.
</p>

<p>
	سنتحدث عن المُغلِّفات بالتفصيل لاحقًا، وكل ما عليك معرفته حاليًا هو أن <code>unwrap_or_else</code> ستمرّر القيمة الداخلية للقيمة <code>Err</code> -وهي في هذه الحالة السلسلة النصية "not enough arguments" التي أضفناها في الشيفرة 9- إلى مغلّفنا ضمن الوسيط <code>err</code> الموجود بين الخطين الشاقوليين (<code>|</code>)، ومن ثم تستطيع الشيفرة البرمجية الموجودة في المغلّف استخدام القيمة الموجودة في <code>err</code> عند تنفيذها.
</p>

<p>
	أضفنا سطر<code>use</code> جديد لإضافة <code>process</code> من المكتبة القياسية إلى النطاق. تُنفَّذ الشيفرة البرمجية الموجودة في المغلّف في <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1920/" rel="">حالة الخطأ</a> ضمن سطرين فقط: نطبع قيمة <code>err</code> ومن ثم نستدعي <code>process::exit</code>. توقف الدالة <code>process::exit</code> البرنامج مباشرةً وتُعيد العدد المُمرّر إليها بمثابة رمز حالة خروج. تشابه العملية استخدام <code>panic!‎</code> في الشيفرة 8، إلا أننا لا نحصل على الخرج الإضافي بعد الآن. دعنا نجرّب الأمر:
</p>

<pre class="ipsCode">$ cargo run
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished dev [unoptimized + debuginfo] target(s) in 0.48s
     Running `target/debug/minigrep`
Problem parsing arguments: not enough arguments
</pre>

<p>
	عظيم، فالرسالة التي نحصل عليها الآن مفهومةً أكثر للمستخدمين.
</p>

<h2>
	استخراج المنطق من الدالة main
</h2>

<p>
	الآن وبعد انتهائنا من إعادة بناء التعليمات البرمجية الخاصة بالحصول على الوسطاء، دعنا ننتقل إلى منطق البرنامج، إذ علينا أن نستخرج المنطق إلى دالةٍ نسميها <code>run</code> كما ذكرنا سابقًا، بحيث تحتوي على الشيفرة البرمجية الموجودة في <code>main</code> حاليًا مع استثناء الشيفرة البرمجية المخصصة لضبط البرنامج، أو التعامل مع الأخطاء، وبحلول نهاية المهمة هذه يجب أن تكون الدالة <code>main</code> سهلة الفحص والقراءة ومختصرة، وسنكون قادرين على كتابة الاختبارات لجزء المنطق من البرنامج بصورةٍ منفصلة.
</p>

<p>
	توضح الشيفرة 11 الدالة <code>run</code> المُستخلصة، وسنبدأ بإجراء تحسينات صغيرة وتدريجية حاليًا لاستخلاص المنطق إلى الدالة. نبدأ بتعريف الدالة في src/main.rs.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9742_22" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">

    println</span><span class="pun">!(</span><span class="str">"Searching for {}"</span><span class="pun">,</span><span class="pln"> config</span><span class="pun">.</span><span class="pln">query</span><span class="pun">);</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"In file {}"</span><span class="pun">,</span><span class="pln"> config</span><span class="pun">.</span><span class="pln">file_path</span><span class="pun">);</span><span class="pln">

    run</span><span class="pun">(</span><span class="pln">config</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

fn run</span><span class="pun">(</span><span class="pln">config</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Config</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let contents </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">::</span><span class="pln">read_to_string</span><span class="pun">(</span><span class="pln">config</span><span class="pun">.</span><span class="pln">file_path</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">expect</span><span class="pun">(</span><span class="str">"Should have been able to read the file"</span><span class="pun">);</span><span class="pln">

    println</span><span class="pun">!(</span><span class="str">"With text:\n{contents}"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// --snip--</span></pre>

<p>
	[الشيفرة 11: استخراج الدالة <code>run</code> التي تحتوي على بقية منطق البرنامج]
</p>

<p>
	تحتوي الدالة <code>run</code> الآن على جميع المنطق المتبقي في <code>main</code> بدءًا من قراءة الملف، وتأخذ الدالة <code>run</code> نسخةً من الهيكل <code>Config</code> مثل وسيط.
</p>

<h3>
	إعادة الأخطاء من الدالة run
</h3>

<p>
	أصبح بإمكاننا -بعد فصل منطق البرنامج في الدالة <code>run</code>- تحسين التعامل مع الأخطاء كما فعلنا بالدالة <code>Config::build</code> في الشيفرة 9. تُعيد الدالة <code>run</code> القيمة <code>Result&lt;T, E&gt;‎</code> بعد حصول خطأ بدلًا من السماح للبرنامج بالهلع باستدعاء <code>expect</code>، وسيسمح لنا ذلك بدعم المنطق الخاص بالتعامل مع الأخطاء في <code>main</code> بطريقة سهلة الاستخدام. توضح الشيفرة 12 التغييرات الواجب إجرائها ومحتوى الدالة <code>run</code>.
</p>

<p>
	اسم الدالة: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9742_24" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">error</span><span class="pun">::</span><span class="typ">Error</span><span class="pun">;</span><span class="pln">

</span><span class="com">// --snip--</span><span class="pln">

fn run</span><span class="pun">(</span><span class="pln">config</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Config</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Result</span><span class="pun">&lt;(),</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">Error</span><span class="pun">&gt;&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let contents </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">::</span><span class="pln">read_to_string</span><span class="pun">(</span><span class="pln">config</span><span class="pun">.</span><span class="pln">file_path</span><span class="pun">)?;</span><span class="pln">

    println</span><span class="pun">!(</span><span class="str">"With text:\n{contents}"</span><span class="pun">);</span><span class="pln">

    </span><span class="typ">Ok</span><span class="pun">(())</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	[الشيفرة 12: تعديل الدالة <code>run</code> بحيث تعيد القيمة <code>Result</code>]
</p>

<p>
	أجرينا ثلاثة تعديلات هنا؛ إذ عدّلنا أولًا القيمة المُعادة من الدالة <code>run</code> إلى النوع <code>Result&lt;(), Box&lt;dyn Error&gt;&gt;‎</code>، فقد أعادت هذه الدالة سابقًا نوع الوحدة unit type <code>()</code>، إلا أننا نُبقي هذا النوع مثل قيمة مُعادة في حالة <code>Ok</code>.
</p>

<p>
	استخدمنا كائن السمة <code>Box&lt;dyn Error&gt;‎</code> لنوع الخطأ (وقد أضفنا <code>std::error::Error</code> إلى النطاق باستخدام التعليمة <code>use</code> في الأعلى). سنغطّي كائنات السمة لاحقًا، ويكفي الآن معرفتك أن <code>Box&lt;dyn Error&gt;‎</code> تعني أن الدالة ستُعيد نوعًا يطبّق السمة <code>Error</code>، إلا أن تحديد نوع القيمة المُعادة ليس ضروريًا. يمنحنا ذلك مرونة إعادة قيم الخطأ التي قد تكون من أنواع مختلفة في حالات فشل مختلفة، والكلمة المفتاحية <code>dyn</code> هي اختصار للكلمة "ديناميكي dynamic".
</p>

<p>
	يتمثّل التعديل الثاني بإزالة استدعاء <code>expect</code> واستبداله بالعامل <code>?</code>، الذي شرحناه سابقًا هنا؛ فبدلًا من استخدام الماكرو <code>panic!‎</code> على الخطأ، يُعيد العامل <code>?</code> قيمة الخطأ من الدالة الحالية للشيفرة البرمجية المستدعية لكي تتعامل معه.
</p>

<p>
	ثالثًا، تُعيد الدالة <code>run</code> الآن قيمة <code>Ok</code> في حالة النجاح. صرّحنا عن نوع نجاح الدالة <code>run</code> في بصمتها على النحو التالي: <code>()</code>، مما يعني أننا بحاجة تغليف قيمة نوع الوحدة في قيمة <code>Ok</code>. قد تبدو طريقة الكتابة <code>Ok(())‎</code> هذه غريبة قليلًا، إلا أن استخدام <code>()</code> بهذا الشكل هو طريقة اصطلاحية للإشارة إلى أننا نستدعي <code>run</code> من أجل تأثيرها الجانبي فقط، إذ أنها لا تُعيد قيمةً نستخدمها.
</p>

<p>
	ستُصرَّف الشيفرة البرمجية السابقة عند تنفيذها، إلا أننا سنحصل على التحذير التالي:
</p>

<pre class="ipsCode">$ cargo run the poem.txt
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
warning: unused `Result` that must be used
  --&gt; src/main.rs:19:5
   |
19 |     run(config);
   |     ^^^^^^^^^^^^
   |
   = note: `#[warn(unused_must_use)]` on by default
   = note: this `Result` may be an `Err` variant, which should be handled

warning: `minigrep` (bin "minigrep") generated 1 warning
    Finished dev [unoptimized + debuginfo] target(s) in 0.71s
     Running `target/debug/minigrep the poem.txt`
Searching for the
In file poem.txt
With text:
I'm nobody! Who are you?
Are you nobody, too?
Then there's a pair of us - don't tell!
They'd banish us, you know.

How dreary to be somebody!
How public, like a frog
To tell your name the livelong day
To an admiring bog!
</pre>

<p>
	تخبرنا رست أن شيفرتنا البرمجية تتجاهل قيمة <code>Result</code>، وأن قيمة <code>Result</code> قد تشير إلى حصول خطأ ما، إلا أننا لا نتحقق من حدوث خطأ، ويذكّرنا المصرّف بأنه من الأفضل لنا كتابة شيفرة برمجية للتعامل مع الأخطاء هنا. دعنا نصحّح هذه المشكلة الآن.
</p>

<h3>
	التعامل مع الأخطاء المُعادة من الدالة run في الدالة main
</h3>

<p>
	سنتحقق من الأحطاء ونتعامل معها باستخدام طرق مماثلة للطرق التي استخدمناها مع <code>Config::build</code> في الشيفرة 10، مع اختلاف بسيط:
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9742_27" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">

    println</span><span class="pun">!(</span><span class="str">"Searching for {}"</span><span class="pun">,</span><span class="pln"> config</span><span class="pun">.</span><span class="pln">query</span><span class="pun">);</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"In file {}"</span><span class="pun">,</span><span class="pln"> config</span><span class="pun">.</span><span class="pln">file_path</span><span class="pun">);</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> let </span><span class="typ">Err</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> run</span><span class="pun">(</span><span class="pln">config</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"Application error: {e}"</span><span class="pun">);</span><span class="pln">
        process</span><span class="pun">::</span><span class="pln">exit</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نستخدم <code>if let</code> بدلًا من <code>unwrap_or_else</code> للتحقق فيما إذا كانت الدالة <code>run</code> تُعيد قيمة <code>Err</code> ونستدعي<code>process::exit(1)‎</code> إذا كانت هذه الحالة محققة. لا تُعيد الدالة <code>run</code> قيمة نحتاج لفك التغليف عنها unwrap باستخدام <code>unwrap</code> بالطريقة ذاتها التي تُعيد فيها الدالة <code>Config::build</code> نسخةً من <code>Config</code>. نهتم فقط بحالة حصول خطأ والتعرف عليه لأن <code>run</code> تُعيد <code>()</code> في حالة النجاح، لذا لا نحتاج من <code>unwrap_or_else</code> أن تُعيد القيمة المفكوك تغليفها، والتي هي ببساطة <code>()</code>.
</p>

<p>
	محتوى تعليمات <code>if let</code> ودوال <code>unwrap_or_else</code> مماثلة في الحالتين: إذ نطبع الخطأ، ثم نغادر البرنامج.
</p>

<h2>
	تجزئة الشيفرة البرمجية إلى وحدة مكتبة مصرفة
</h2>

<p>
	يبدو مشروع <code>minigrep</code> جيّدًا حتى اللحظة. سنجزّء الآن ملف src/main.rs ونضع جزءًا من الشيفرة البرمجية في ملف "src/lib.rs"، إذ يمكننا بهذه الطريقة اختبار الشيفرة البرمجية مع ملف src/main.rs لا ينجز العديد من المهام.
</p>

<p>
	دعنا ننقل الشيفرة البرمجية غير الموجودة في الدالة <code>main</code> من الملف src/main.rs إلى الملف src/lib.rs، والتي تتضمن:
</p>

<ul>
	<li>
		تعريف الدالة <code>run</code>.
	</li>
	<li>
		تعليمات <code>use</code> المرتبطة بالشيفرة البرمجية التي سننقلها.
	</li>
	<li>
		تعريف الهيكل <code>Config</code>.
	</li>
	<li>
		تعريف دالة <code>Config::build</code>.
	</li>
</ul>

<p>
	يجب أن يحتوي الملف src/lib.rs على البصمات الموضحة في الشيفرة 13 (أهملنا محتوى الدوال لاختصار طول الشيفرة). لاحظ أن الشيفرة البرمجية لا تُصرَّف حتى نعدّل الملف src/main.rs وهو ما نفعله في الشيفرة 14.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9742_29" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">error</span><span class="pun">::</span><span class="typ">Error</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">fs</span><span class="pun">;</span><span class="pln">

pub </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Config</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pub query</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
    pub file_path</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

impl </span><span class="typ">Config</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pub fn build</span><span class="pun">(</span><span class="pln">args</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;[</span><span class="typ">String</span><span class="pun">])</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Result</span><span class="pun">&lt;</span><span class="typ">Config</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="str">'</span><span class="kwd">static</span><span class="pln"> str</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// --snip--</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

pub fn run</span><span class="pun">(</span><span class="pln">config</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Config</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Result</span><span class="pun">&lt;(),</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">Error</span><span class="pun">&gt;&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	[الشيفرة 13: نقل <code>Config</code> و <code>run</code> إلى src/lib.rs]
</p>

<p>
	استخدمنا الكلمة المفتاحية <code>pub</code> هنا بحرية في كل من الهيكل <code>Config</code> وحقوله وتابعه <code>build</code> وعلى الدالة <code>run</code>. أصبح لدينا وحدة مكتبة مصرَّفة library crate بواجهة برمجية عامة public <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> يمكننا استخدامها.
</p>

<p>
	نحتاج إضافة الشيفرة البرمجية التي نقلناها إلى الملف src/lib.rs إلى نطاق الوحدة الثنائية المصرفة binary crate في الملف src/main.rs كما هو موضح في الشيفرة 14:
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9742_31" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">env</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">process</span><span class="pun">;</span><span class="pln">

use minigrep</span><span class="pun">::</span><span class="typ">Config</span><span class="pun">;</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> let </span><span class="typ">Err</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> minigrep</span><span class="pun">::</span><span class="pln">run</span><span class="pun">(</span><span class="pln">config</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// --snip--</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	[الشيفرة 14: استخدام وحدة المكتبة المصرفة <code>minigrep</code> في src/main.rs]
</p>

<p>
	أضفنا السطر <code>use minigrep::Config</code> لإضافة النوع <code>Config</code> من وحدة المكتبة المصرّفة إلى نطاق الوحدة الثنائية المصرّفة، وأسبقنا prefix الدالة <code>run</code> باسم الوحدة المصرفة crate. يجب أن تكون وظائف البرنامج مترابطة مع بعضها بعضًا الآن، وأن تعمل بنجاح، لذا نفّذ البرنامج باستخدام <code>cargo run</code> وتأكد من أن كل شيء يعمل كما هو مطلوب.
</p>

<p>
	أخيرًا، كان هذا عملًا شاقًا، إلا أننا بدأنا بأساس يضمن لنا النجاح في المستقبل، إذ أصبح التعامل مع الأخطاء الآن سهلًا وقد جعلنا من شيفرتنا البرمجية أكثر معيارية. سنعمل في الملف src/lib.rs بصورةٍ أساسية من الآن وصاعدًا.
</p>

<p>
	دعنا نستفيد من المعيارية الجديدة في برنامجنا بتحقيق شيءٍ كان من الممكن أن يكون صعب التحقيق في الشيفرة البرمجية القادمة، إلا أنه أصبح أسهل بالشيفرة البرمجية الجديدة، ألا وهو كتابة الاختبارات.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch12-00-an-io-project.html" rel="external nofollow">An I/O Project: Building a Command Line Program</a> من كتاب <a href="https://doc.rust-lang.org/stable/book/title-page.html/" rel="external nofollow">The Rust Programming Language</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-r1978/" rel="">كتابة برنامج سطر أوامر بلغة رست: اختبار البرنامج</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-command-line-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AF%D8%AE%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%B1%D8%AC-r1969/" rel="">كتابة برنامج سطر أوامر Command Line بلغة رست Rust: التعامل مع الدخل والخرج</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%87%D9%8A%D8%A7%D9%83%D9%84-structs-%D9%84%D8%AA%D9%86%D8%B8%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1849/" rel="">استخدام الهياكل structs لتنظيم البيانات في لغة رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%AD%D9%84-%D8%A7%D9%84%D9%85%D8%B4%D9%83%D9%84%D8%A7%D8%AA-%D9%88%D8%A3%D9%87%D9%85%D9%8A%D8%AA%D9%87%D8%A7-%D9%81%D9%8A-%D8%A7%D8%AD%D8%AA%D8%B1%D8%A7%D9%81-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r760/" rel="">حل المشكلات وأهميتها في احتراف البرمجة</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1974</guid><pubDate>Wed, 10 May 2023 13:00:00 +0000</pubDate></item><item><title>&#x643;&#x62A;&#x627;&#x628;&#x629; &#x628;&#x631;&#x646;&#x627;&#x645;&#x62C; &#x633;&#x637;&#x631; &#x623;&#x648;&#x627;&#x645;&#x631; Command Line &#x628;&#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A; Rust: &#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x627;&#x644;&#x62F;&#x62E;&#x644; &#x648;&#x627;&#x644;&#x62E;&#x631;&#x62C;</title><link>https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-command-line-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AF%D8%AE%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%B1%D8%AC-r1969/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_05/----Command-Line---Rust-----.png.1ae21d45fc1aca01bf5d7dff8902f702.png" /></p>
<p>
	يمثّل هذا المقال تطبيقًا لجميع المهارات التي تعلمتها حتى الآن باتباعك لهذه السلسلة <a href="https://academy.hsoub.com/tags/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9%20%D8%A8%D9%84%D8%BA%D8%A9%20%D8%B1%D8%B3%D8%AA/" rel="">البرمجة بلغة رست</a>، ونظرةً عمليةً إلى المزيد من المزايا الموجودة في المكتبة القياسية Standard Library. سنبني سويًّا أداة سطر أوامر command line tool تتفاعل مع ملف وتُجري عمليات الدخل والخرج باستخدام سطر الأوامر للتمرُّن على بعض مفاهيم رست التي تعلمتها حتى هذه اللحظة.
</p>

<p>
	تجعل السرعة والأمان والخرج الثنائي الوحيد ودعم مختلف المنصات cross-platform من رست لغةً مثالية لإنشاء أدوات السطر البرمجي، لذا سيكون مشروعنا هو إصدار خاص من أداة بحث سطر الأوامر الكلاسيكية "<a href="https://academy.hsoub.com/devops/linux/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%A3%D9%85%D8%B1-grep-%D9%81%D9%8A-%D9%84%D9%8A%D9%86%D9%83%D8%B3-r566/" rel="">grep</a>" (اختصارًا للبحث العام باستخدام التعابير النمطية والطباعة ‎<strong>g</strong>lobally search a <strong>r</strong>egular <strong>e</strong>xpression and <strong>p</strong>rint). يبحث "grep" في حالات الاستخدام الأسهل على سلسلة نصية string محددة داخل ملف معين، ولتحقيق ذلك يأخذ "grep" مسار الملف وسلسلة نصية مثل وسطاء له، ثم يقرأ الملف ويجد الأسطر التي تحتوي على وسيط السلسلة النصية داخل الملف ويطبع هذه الأسطر.
</p>

<p>
	سنوضّح لك كيف ستستخدم أداة سطر الأوامر لخصائص سطر الأوامر وهو ما تستخدمه العديد من أدوات سطر الأوامر الأخرى، إذ سنقرأ قيمة متغير البيئة environment variable للسماح للمستخدم بضبط سلوك أداتنا، كما أننا سنطبع رسالة خطأ إلى مجرى الخطأ القياسي الخاص بالطرفية standard error console stream -أو اختصارًا stderr- بدلًا من الخرج القياسي standard output -أو اختصارًا stdout. لذلك، يمكن للمستخدم مثلًا إعادة توجيه الخرج الناجح إلى ملف بحيث يظل قادرًا على رؤية رسالة الخطأ على الشاشة في الوقت ذاته.
</p>

<p>
	أنشأ عضو من مجتمع رست يدعى آندرو غالانت Andrew Gallant إصدارًا سريعًا ومليئًا بالمزايا من grep وأطلق عليه تسمية ripgrep. سيكون إصدارنا الذي سننشئه في هذه المقالة أكثر بساطةً إلا أن هذه المقالة ستمنحك بعض الأساسيات التي تحتاجها لتفهم المشاريع العملية الواقعية مثل ripgrep.
</p>

<p>
	سيجمع مشروع grep الخاص بنا عددًا من المفاهيم التي تعلمناها سابقًا:
</p>

<ul>
	<li>
		تنظيم الشيفرة البرمجية (استخدام ما تعلمناه بخصوص الوحدات modules في مقال <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AD%D8%B2%D9%85-packages-%D9%88%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B5%D8%B1%D9%81%D8%A9-crates-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1853/" rel="">الحزم packages والوحدات المصرفة crates في لغة رست Rust</a>)
	</li>
	<li>
		استخدام الأشعة vectors والسلاسل النصية (في مقال <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D9%84%D8%A7%D8%A6%D8%AD%D8%A9-%D9%85%D9%86-%D8%A7%D9%84%D9%82%D9%8A%D9%85-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%A3%D8%B4%D8%B9%D8%A9-vectors-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1875/" rel="">تخزين لائحة من القيم باستخدام الأشعة Vectors في لغة رست Rust</a>)
	</li>
	<li>
		التعامل مع الأخطاء (في مقال <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1920/" rel="">الأخطاء والتعامل معها في لغة رست Rust</a>)
	</li>
	<li>
		استخدام السمات traits ودورات الحياة lifetimes عند الحاجة (في مقال <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%B3%D9%85%D8%A7%D8%AA-traits-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1951/" rel="">السمات Traits في لغة رست Rust</a> ومقال <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%AD%D9%82%D9%82-%D9%85%D9%86-%D8%A7%D9%84%D9%85%D8%B1%D8%A7%D8%AC%D8%B9-references-%D8%B9%D8%A8%D8%B1-%D8%AF%D9%88%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%AD%D9%8A%D8%A7%D8%A9-lifetimes-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1952/" rel="">التحقق من المراجع References عبر دورات الحياة Lifetimes في لغة رست</a>)
	</li>
	<li>
		كتابة الاختبارات (في مقال <a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1959/" rel="">كتابة الاختبارات في لغة رست Rust</a>)
	</li>
</ul>

<p>
	سنتكلم أيضًا بإيجاز عن المغلّفات closures والمكرّرات iterators وسمة الكائنات trait objects، إلا أننا سنتكلم عن هذه المفاهيم بالتفصيل لاحقًا.
</p>

<h2>
	الحصول على الوسطاء من سطر الأوامر
</h2>

<p>
	دعنا نُنشئ مشروعًا جديدًا باستخدام <code>cargo new</code> كما اعتدنا، سنسمّي مشروعنا باسم "minigrep" للتمييز بينه وبين الأداة <code>grep</code> التي قد تكون موجودةً على نظامك مسبقًا.
</p>

<pre class="ipsCode">$ cargo new minigrep
     Created binary (application) `minigrep` project
$ cd minigrep
</pre>

<p>
	المهمة الأولى هي بجعل <code>minigrep</code> يقبل وسيطين من <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471/" rel="">سطر الأوامر</a>، ألا وهما مسار الملف وسلسلة نصية للبحث عنها، أي أننا نريد أن نكون قادرين على تنفيذ برنامجنا باستخدام <code>cargo run</code> متبوعًا بشرطتين للدلالة على أن الوسطاء التي ستتبعها تنتمي للبرنامج الذي نريد أن ننفذه وليس لكارجو cargo، سيكون لدينا وسيطان أولهما سلسلة نصية للبحث عنها وثانيهما مسار الملف الذي نريد البحث بداخله على النحو التالي:
</p>

<pre class="ipsCode">$ cargo run -- searchstring example-filename.txt
</pre>

<p>
	لا يستطيع البرنامج المولَّد باستخدام <code>cargo new</code> حاليًا معالجة الوسطاء التي نمرّرها له، ويمكن أن تساعدك بعض المكتبات الموجودة على <a href="https://crates.io/" rel="external nofollow">crates.io</a> بكتابة برامج تقبل وسطاء سطر الأوامر، إلا أننا سنطبّق هذا الأمر بأنفسنا بهدف التعلُّم.
</p>

<h3>
	قراءة قيم الوسطاء
</h3>

<p>
	سنحتاج لاستخدام الدالة <code>std::env::args</code> الموجودة في مكتبة رست القياسية لتمكين <code>minigrep</code> من قراءة قيم وسطاء سطر الأوامر التي نمرّرها إليه، إذ تُعيد هذه الدالة مكرّرًا iterator إلى وسطاء سطر الأوامر الممرّرة إلى <code>minigrep</code>. سنغطّي المكررات لاحقًا، إلا أنه من الكافي الآن معرفتك معلومتين بخصوص المكررات: تنتج المكررات مجموعةً من القيم، ويمكننا استدعاء التابع <code>collect</code> على مكرّر لتحويله إلى تجميعة collection مثل الأشعة التي تحتوي جميع العناصر التي تنتجها المكررات.
</p>

<p>
	تسمح الشيفرة 1 لبرنامجك <code>minigrep</code> بقراءة أي وسطاء سطر أوامر تمررها إليه، ثمّ تجمّع القيم في شعاع.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_954_16" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">env</span><span class="pun">;</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let args</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> env</span><span class="pun">::</span><span class="pln">args</span><span class="pun">().</span><span class="pln">collect</span><span class="pun">();</span><span class="pln">
    dbg</span><span class="pun">!(</span><span class="pln">args</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 1: تجميع وسطاء سطر الأوامر في شعاع وطباعتها]
</p>

<p>
	نُضيف أوّلًا الوحدة <code>std::env</code> إلى النطاق scope باستخدام تعليمة <code>use</code>، بحيث يمكننا استخدام الدالة <code>args</code> المحتواة داخلها. لاحظ أن الدالة <code>std::env::args</code> متداخلة nested مع مستويين من الوحدات. سنختار إحضار الوحدة الأب إلى النطاق بدلًا من الدالة كما ناقشنا سابقًا في الحالات التي تكون فيها الدالة المطلوبة متداخلة مع أكثر من وحدة واحدة، ويمكننا بذلك استخدام الدوال الأخرى من <code>std::env</code>، كما أن هذه الطريقة أقل غموضًا من إضافة <code>use std::env::args</code>، ثم استدعاء الدالة بكتابة <code>args</code> فقط لأن <code>args</code> قد يُنظر إليها بكونها دالة معرّفة بالوحدة الحالية بصورةٍ خاطئة.
</p>

<p>
	<strong>ملاحظة:</strong> ستهلع <code>std::env::args</code> إذا احتوى أي وسيط على <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D8%A8%D8%AA%D8%B1%D9%85%D9%8A%D8%B2-utf-8-%D8%AF%D8%A7%D8%AE%D9%84-%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1876/" rel="">يونيكود</a> غير صالح، وإذا احتاج البرنامج لقبول الوسطاء التي تحتوي على يونيكود غير صالح، فعليك استخدام <code>std::env::args_os</code> بدلًا منها، إذ تعيد هذه الدالة مكرّرًا ينتج قيم <code>OsString</code> بدلًا من قيم <code>String</code>، وقد اخترنا استخدام <code>std::env::args</code> هنا لبساطتها، ونظرًا لاختلاف قيم <code>OsString</code> بحسب المنصة وهي أكثر تعقيدًا في التعامل معها مقارنةً بقيم <code>String</code>.
</p>

<p>
	نستدعي <code>env::args</code> في السطر الأول من <code>main</code> ومن ثم نستخدم <code>collect</code> مباشرةً لتحويل المكرّر إلى شعاع يحتوي على جميع القيم الموجودة في المكرّر، ويمكننا استخدام <code>collect</code> هنا لإنشاء عدة أنواع من التجميعات، لذا يجب أن نشير صراحةً لنوع <code>args</code> لتحديد أننا نريد شعاع من السلاسل النصية، وعلى الرغم من أن تحديد الأنواع في رست نادر، إلا أن <code>collect</code> دالة يجب أن تحدد فيها النوع عادةً، لأن رست لا تستطيع استنتاج نوع التجميعة التي تريدها.
</p>

<p>
	أخيرًا، نطبع الشعاع باستخدام ماكرو تنقيح الأخطاء debug macro. دعنا نجرّب تنفيذ الشيفرة البرمجية أولًا دون استخدام أي وسطاء ومن ثم باستخدام وسيطين:
</p>

<pre class="ipsCode">$ cargo run
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished dev [unoptimized + debuginfo] target(s) in 0.61s
     Running `target/debug/minigrep`
[src/main.rs:5] args = [
    "target/debug/minigrep",
]
</pre>

<pre class="ipsCode">$ cargo run -- needle haystack
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished dev [unoptimized + debuginfo] target(s) in 1.57s
     Running `target/debug/minigrep needle haystack`
[src/main.rs:5] args = [
    "target/debug/minigrep",
    "needle",
    "haystack",
]
</pre>

<p>
	لاحظ أن القيمة الأولى في الشعاع هي <code>"target/debug/minigrep"</code> وهو اسم ملفنا التنفيذي، وهذا يطابق سلوك لائحة الوسطاء في لغة <a href="https://academy.hsoub.com/programming/c/" rel="">سي C</a> بالسماح للبرامج باستخدام الاسم الذي كان السبب في بدء تنفيذها، ومن الملائم عادةً الحصول على اسم البرنامج في حال أردت طباعته ضمن رسائل، أو تعديل سلوك البرنامج المبني على اسم سطر الأوامر البديل command line alias الذي نستخدمه لبدء تشغيل البرنامج، وسنتجاهله الآن ونحفظ فقط أول وسيطين نستخدمهما.
</p>

<h3>
	حفظ قيم الوسطاء في متغيرات
</h3>

<p>
	يستطيع البرنامج حاليًا الحصول على القيم المحدّدة مثل وسطاء سطر أوامر، أما الآن فنحن بحاجة لحفظ قيم الوسيطين في متغيرين بحيث يمكننا استخدام المتغيرين ضمن بقية البرنامج، وهو ما فعلناه في الشيفرة 2.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_954_18" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">env</span><span class="pun">;</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let args</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> env</span><span class="pun">::</span><span class="pln">args</span><span class="pun">().</span><span class="pln">collect</span><span class="pun">();</span><span class="pln">

    let query </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">args</span><span class="pun">[</span><span class="lit">1</span><span class="pun">];</span><span class="pln">
    let file_path </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">args</span><span class="pun">[</span><span class="lit">2</span><span class="pun">];</span><span class="pln">

    println</span><span class="pun">!(</span><span class="str">"Searching for {}"</span><span class="pun">,</span><span class="pln"> query</span><span class="pun">);</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"In file {}"</span><span class="pun">,</span><span class="pln"> file_path</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 2: إنشاء متغيرين لتخزين وسيط السلسلة النصية ووسيط مسار الملف]
</p>

<p>
	يحتلّ اسم البرنامج القيمة الأولى في الشعاع عند <code>args[0]‎</code> كما رأينا سابقًا عندما طبعنا الشعاع، لذا نبدأ من الدليل "1" إذ أن الوسيط الأول للبرنامج <code>minigrep</code> هو السلسلة النصية التي سنبحث عنها، لذا نضع مرجعًا reference على أول وسيط في المتغير <code>query</code>، بينما يمثل الوسيط الثاني مسار الملف، لذا نضع مرجعًا عليه في المتغير <code>file_path</code>.
</p>

<p>
	نطبع مؤقتًا قيم المتغيرَين لنتأكد من أن الشيفرة البرمجية تعمل وفق ما هو متوقع. دعنا ننفذ البرنامج مجددًا بالوسيطين <code>test</code> و <code>sample.txt</code>:
</p>

<pre class="ipsCode">$ cargo run -- test sample.txt
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished dev [unoptimized + debuginfo] target(s) in 0.0s
     Running `target/debug/minigrep test sample.txt`
Searching for test
In file sample.txt
</pre>

<p>
	عظيم، يعمل برنامجنا بنجاح، إذ تُمرّر قيم الوسطاء التي نحتاجها وتُحفظ في المتغيرات المناسبة. سنضيف لاحقًا شيفرةً برمجيةً للتعامل مع الأخطاء في حالات استخدام خاطئة محتملة، مثل الحالة التي لا يدخل فيها المستخدم أي وسطاء، والتي سنتجاهلها الآن ونبدأ بالعمل على إضافة شيفرة برمجية لقراء الملف بدلًا من ذلك.
</p>

<h2>
	قراءة ملف
</h2>

<p>
	سنضيف الآن إمكانية قراءة الملف المحدد في الوسيط <code>file_path</code>. نحتاج أولًا لملف تجريبي لتجربة البرنامج باستخدامه، وسنستخدم ملفًا يحتوي على نصٍ قصير يحتوي على عدّة أسطر مع كلمات مكرّرة. تحتوي الشيفرة 3 على قصيدة لإيميلي ديكنز Emily Dickinson وهو ما سنستخدمه هنا. أنشئ ملفًا يدعى "poem.txt" في مستوى جذر المشروع وأدخل قصيدة "أنا لا أحد! من أنت؟ I'm Nobody! Who are you?‎".
</p>

<p>
	اسم الملف: poem.txt
</p>

<pre class="ipsCode">I'm nobody! Who are you?
Are you nobody, too?
Then there's a pair of us - don't tell!
They'd banish us, you know.

How dreary to be somebody!
How public, like a frog
To tell your name the livelong day
To an admiring bog!
</pre>

<p style="text-align: center;">
	[الشيفرة 3: قصيدة إيميلي ديكنز تمثّل ملف تجريبي مناسب]
</p>

<p>
	بعد تهيئتك للنص، عدّل الملف "src/main.rs" وضِف شيفرة برمجية لقراءة الملف كما هو موضح في الشيفرة 4.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_954_20" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">env</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">fs</span><span class="pun">;</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"In file {}"</span><span class="pun">,</span><span class="pln"> file_path</span><span class="pun">);</span><span class="pln">

    let contents </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">::</span><span class="pln">read_to_string</span><span class="pun">(</span><span class="pln">file_path</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">expect</span><span class="pun">(</span><span class="str">"Should have been able to read the file"</span><span class="pun">);</span><span class="pln">

    println</span><span class="pun">!(</span><span class="str">"With text:\n{contents}"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 4: قراءة محتويات الملف المحدّد وفق الوسيط الثاني]
</p>

<p>
	أضفنا أولًا جزءًا متعلقًا بالبرنامج من المكتبة القياسية باستخدام تعليمة <code>use</code> إلى النطاق، لأننا نحتاج إلى <code>std::fs</code> إلى <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r1334/" rel="">التعامل مع الملفات</a>.
</p>

<p>
	تأخذ التعليمة <code>fs::read_to_string</code> الجديدة في الدالة <code>main</code> القيمة <code>file_path</code>، ثم تفتح ذلك الملف وتُعيد قيمةً من النوع <code>std::io::Result&lt;String&gt;‎</code> تمثّل محتويات الملف.
</p>

<p>
	أضفنا مجددًا تعليمة <code>println!‎</code> مؤقتة تطبع قيمة <code>contents</code> بعد قراءة الملف، حتى نتأكد من عمل البرنامج بصورةٍ صحيحة.
</p>

<p>
	لننفّذ هذه الشيفرة البرمجية باستخدام أي سلسلة نصية تمثّل وسيط سطر أوامر أول (لأننا لم نطبق جزء البحث عن السلسلة النصية بعد) والملف "poem.txt" بمثابة وسيط ثاني:
</p>

<pre class="ipsCode">$ cargo run -- the poem.txt
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished dev [unoptimized + debuginfo] target(s) in 0.0s
     Running `target/debug/minigrep the poem.txt`
Searching for the
In file poem.txt
With text:
I'm nobody! Who are you?
Are you nobody, too?
Then there's a pair of us - don't tell!
They'd banish us, you know.

How dreary to be somebody!
How public, like a frog
To tell your name the livelong day
To an admiring bog!
</pre>

<p>
	عظيم، تقرأ الآن الشيفرة البرمجية محتويات الملف ثم تطبعها، إلا أن الشيفرة البرمجية تحتوي على بعض الثغرات، إذ تحتوي الدالة <code>main</code> الآن على عدّة مسؤوليات، ومن الأفضل عمومًا استخدام دالة واحدة لمسؤولية واحدة للحصول على دوال أسهل بالتعامل وأوضح، والمشكلة الثانية هي أننا لم نتعامل مع الأخطاء كما ينبغي لنا، إلا أن البرنامج ما زال صغيرًا وبالتالي لا تشكل هذه المشاكل تهديدًا كبيرًا، لكنها ستصبح صعبة الحل مع زيادة حجم البرنامج، فمن الأفضل إعادة بناء التعليمات البرمجية refactor بمرحلة مبكرة من تطوير البرنامج لأن إعادة بناء التعليمات البرمجية سيكون أسهل بكثير من كميات قليلة من الشيفرات البرمجية، لذا دعنا نفعل ذلك تاليًا في المقال التالي.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch12-03-improving-error-handling-and-modularity.html" rel="external nofollow">Refactoring to Improve Modularity and Error Handling</a> من كتاب <a href="https://doc.rust-lang.org/stable/book/title-page.html/" rel="external nofollow">The Rust Programming Language</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A5%D8%B9%D8%A7%D8%AF%D8%A9-%D8%A8%D9%86%D8%A7%D8%A1-%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%84%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-modularity-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-r1974/" rel="">كتابة برنامج سطر أوامر بلغة رست: إعادة بناء التعليمات البرمجية لتحسين النمطية Modularity والتعامل مع الأخطاء</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D9%86%D8%B8%D9%8A%D9%85-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-tests-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1961/" rel="">تنظيم الاختبارات Tests في لغة رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">ما هو سطر الأوامر</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/10-%D8%A3%D9%85%D8%AB%D9%84%D8%A9-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%B9%D9%84%D9%89-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%A3%D8%AF%D8%A7%D8%A9-grep-%D8%AA%D9%81%D9%8A%D8%AF-%D8%A7%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86-r679/" rel="">10 أمثلة عملية على استخدام الأداة Grep تفيد المبرمجين</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1969</guid><pubDate>Wed, 03 May 2023 13:00:00 +0000</pubDate></item><item><title>&#x62A;&#x646;&#x638;&#x64A;&#x645; &#x627;&#x644;&#x627;&#x62E;&#x62A;&#x628;&#x627;&#x631;&#x627;&#x62A; Tests &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A; Rust</title><link>https://academy.hsoub.com/programming/rust/%D8%AA%D9%86%D8%B8%D9%8A%D9%85-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-tests-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1961/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_04/--Integration-Tests---Module-Tests----Rust.jpg.3521ba349ba605b40bf29adc58362c92.jpg" /></p>
<p>
	يُعد اختبار الشيفرة البرمجية كما ذكرنا سابقًا في مقال <a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1959/" rel="">كتابة الاختبارات في لغة رست Rust</a> ممارسةً معقدة، ويستخدم الناس الاختبارات بطرق ومصطلحات مختلفة، إضافةً إلى تنظيمها، إلا أن مجتمع مبرمجي لغة رست ينظر إلى الاختبارات بكونها تنتمي إلى أحد التصنيفين الرئيسين: اختبارات الوحدة unit tests واختبارات التكامل integration tests. تعدّ اختبارات الوحدة اختبارات صغيرة ومحددة على وحدة معيّنة منعزلة ويُمكن أن تختبر الواجهات الخاصة private interfaces، بينما تكون اختبارات التكامل خارجية كليًا لمكتبتك وتستخدم شيفرتك البرمجية بالطريقة ذاتها التي تستخدم فيها شيفرة برمجية خارجية اعتيادية شيفرتك البرمجية باستخدام الواجهات العامة public interface وتتضمن غالبًا أكثر من وحدة ضمن الاختبار الواحد.
</p>

<p>
	كتابة نوعَي الاختبارات مهمٌ للتأكد من أن أجزاء في مكتبتك تنجز ما هو مطلوب منها بغض النظر عن باقي الأجزاء.
</p>

<h2>
	اختبارات الوحدة
</h2>

<p>
	الهدف من اختبارات الوحدة هو اختبار كل وحدة من شيفرة برمجية بمعزل عن الشيفرة البرمجية المتبقية، وذلك لتشخيص النقطة التي لا تعمل فيها الشيفرة البرمجية بصورةٍ صحيحة وبدقة، لذلك ستضع اختبارات الوحدة في المجلد "src" في كل ملف مع الشيفرة البرمجة التي تختبرها، ويقتضي الاصطلاح بإنشاء وحدة تدعى <code>tests</code> في كل ملف لاحتواء دوال الاختبار وتوصيف الوحدة باستخدام <code>cfg(test)‎</code>.
</p>

<h3>
	وحدة الاختبارات وتوصيف ‎#[cfg(test)]‎
</h3>

<p>
	يخبر توصيف <code>‎#[cfg(test)]‎</code> على وحدة الاختبارات رست بأنه يجب تصريف وتشغيل شيفرة الاختبار البرمجية فقط عند تنفيذ <code>cargo test</code> وليس عند تنفيذ <code>cargo build</code>، مما يختصر وقتًا من عملية التصريف عندما تريد فقط بناء المكتبة وتوفير المساحة التي سيشغلها الملف المُصرَّف الناتج وذلك لأن الاختبارات غير مُضمّنة به. توضع اختبارات التكامل في مجلد مختلف ولا تستخدم التوصيف <code>‎#[cfg(test)]‎</code>، إلا أنك بحاجة لاستخدام <code>‎#[cfg(test)]‎</code> لتحديد أنها لا يجب أن تُضمَّن في الملف المصرَّف الناتج لأن اختبارات الوحدة موجودة في ملف الشيفرة البرمجية ذاته.
</p>

<p>
	تذكر أن كارجو Cargo ولّد الشيفرة البرمجية التالية لنا عندما ولدنا المشروع <code>adder</code> الجديد سابقًا:
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5060_8" style=""><span class="com">#[cfg(test)]</span><span class="pln">
mod tests </span><span class="pun">{</span><span class="pln">
    </span><span class="com">#[test]</span><span class="pln">
    fn it_works</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let result </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
        assert_eq</span><span class="pun">!(</span><span class="pln">result</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تُولّد هذه الشيفرة البرمجية تلقائيًا على هيئة وحدة اختبار. تمثّل السمة <code>cfg</code> اختصارًا لكلمة الضبط configuration، إذ تُعلم رست أن العنصر الآتي يجب أن يُضمَّن فقط في خيار ضبط معيّن، وفي هذه الحالة فإن خيار الضبط <code>test</code> الموجود في رست لتصريف وتنفيذ الاختبارات. يصرّف كارجو شيفرة الاختبار البرمجية فقط في حال تنفيذ الاختبارات بفعالية باستخدام <code>cargo test</code>، وهذا يتضمّن أي دوال مساعدة يمكن أن تكون داخل هذه الوحدة، إضافةً للدوال الموصّفة باستخدام <code>‎#[test]‎</code>.
</p>

<h3>
	اختبار الدوال الخاصة
</h3>

<p>
	هناك اختلافٌ بين المبرمجين بخصوص الاختبارات وبالأخص اختبار الدوال الخاصة، إذ يعتقد البعض أن الدوال الخاصة يجب أن تُختبر مباشرةً، بينما لا يتفق البعض الآخر مع ذلك، وتجعل <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغات البرمجة</a> اختبار الدوال الخاصة صعبًا أو مستحيلًا، وبغض النظر عمّا تعتقد بخصوص هذا الشأن، تسمح قوانين خصوصية رست لك باختبار الدوال الخاصة. ألقِ نظرةً على الشيفرة 12 التي تحتوي على الشيفرة الخاصة <code>internal_adder</code>.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5060_10" style=""><span class="pln">pub fn add_two</span><span class="pun">(</span><span class="pln">a</span><span class="pun">:</span><span class="pln"> i32</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> i32 </span><span class="pun">{</span><span class="pln">
    internal_adder</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

fn internal_adder</span><span class="pun">(</span><span class="pln">a</span><span class="pun">:</span><span class="pln"> i32</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">:</span><span class="pln"> i32</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> i32 </span><span class="pun">{</span><span class="pln">
    a </span><span class="pun">+</span><span class="pln"> b
</span><span class="pun">}</span><span class="pln">

</span><span class="com">#[cfg(test)]</span><span class="pln">
mod tests </span><span class="pun">{</span><span class="pln">
    use super</span><span class="pun">::*;</span><span class="pln">

    </span><span class="com">#[test]</span><span class="pln">
    fn internal</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        assert_eq</span><span class="pun">!(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> internal_adder</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">));</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 12: اختبار دالة خاصة]
</p>

<p>
	لاحظ أن الدالة <code>internal_adder</code> ليست دالة عامة (لا تحتوي على <code>pub</code>). الاختبارات هي شيفرة برمجية مكتوبة بلغة رست وبالتالي تمثل وحدة <code>tests</code> وحدة اعتيادية، وكما ناقشنا سابقًا يمكن العناصر في الوحدات الابن استخدام العناصر الموجودة في الوحدات الأب، وفي هذا الاختبار نُضيف كل عناصر الوحدات الأب الخاصة بالوحدة <code>test</code> إلى النطاق بكتابة <code>use super::*‎</code>، بحيث يمكننا استدعاء <code>internal_adder</code> فيما بعد. إذا لم تكن مقتنعًا بأن الدوال الخاصة يجب أن تُختَبر فلن تجبرك رست على ذلك.
</p>

<h2>
	اختبارات التكامل Integration Tests
</h2>

<p>
	<a href="https://academy.hsoub.com/questions/11823-%D8%B4%D8%B1%D8%AD-%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-%D8%A3%D8%B3%D8%A7%D9%84%D9%8A%D8%A8-%D9%8A%D8%AA%D9%85-%D8%A5%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D9%87%D8%A7-%D9%84%D9%84%D8%AA%D8%A3%D9%83%D8%AF-%D9%85%D9%86-%D8%B3%D9%84%D8%A7%D9%85%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D9%88%D8%A8%D8%A9-%D9%85%D9%86-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1/" rel="">اختبارات التكامل Integration Tests</a> في رست خارجية external كليًا بالنسبة لمكتبتك، وتستخدم هذه الاختبارات مكتبتك بالطريقة ذاتها لأي شيفرة برمجية، مما يعني أنها يمكن أن تستدعي دوال تشكل جزءًا من الواجهة البرمجية العامة Public <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> للمكتبة. الهدف من هذا النوع من الاختبارات هو اختبار ما إذا كانت أجزاء من مكتبتك تعمل مع بعضها بعضًا بصورةٍ صحيحة، إذ أن بعض الأجزاء من الشيفرة البرمجية قد تعمل بصورةٍ صحيحة لوحدها ولكن تواجه بعض المشاكل عند تكاملها مع أجزاء أخرى، لذا يُغطّي هذا النوع من الاختبارات الشيفرة البرمجية المتكاملة. نحتاج لإنشاء اختبارات التكامل أولًا إلى مجلد tests.
</p>

<h3>
	مجلد tests
</h3>

<p>
	نُنشئ مجلد "tests" في المستوى الأعلى لمجلد مشروعنا بجانب "src"، إذ يتعرّف كارجو على المجلد والاختبارات التي بداخله، ويمكننا إنشاء ملفات اختبار قدر ما شئنا، وسيصرّف كارجو كل ملف اختبار بدوره بكونه وحدة مصرّفة crate منفصلة.
</p>

<p>
	لننشئ اختبار تكامل باستخدام الشيفرة البرمجية الموجودة في الشيفرة 12 الموجودة في الملف src/lib.rs، نبدأ أولًا بإنشاء مجلد tests ونُنشئ ملفًا جديدًا نسميه tests/integration_test.rs. يجب أن يبدو هيكل المجلد كما يلي:
</p>

<pre class="ipsCode">adder
├── Cargo.lock
├── Cargo.toml
├── src
│   └── lib.rs
└── tests
    └── integration_test.rs
</pre>

<p>
	أدخل الشيفرة البرمجية في الشيفرة 13 إلى الملف tests/integration_test.rs:
</p>

<p>
	اسم الملف: tests/integration_test.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5060_12" style=""><span class="pln">use adder</span><span class="pun">;</span><span class="pln">

</span><span class="com">#[test]</span><span class="pln">
fn it_adds_two</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    assert_eq</span><span class="pun">!(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> adder</span><span class="pun">::</span><span class="pln">add_two</span><span class="pun">(</span><span class="lit">2</span><span class="pun">));</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 13: اختبار تكامل لدالة في الوحدة المصرَّفة <code>adder</code>]
</p>

<p>
	يمثل كل ملف في المجلد "tests" وحدة مصرفة منعزلة، لذا يجب علينا إضافة مكتبتنا إلى نطاق وحدة الاختبار المصرَّفة، ونُضيف لهذا السبب <code>use adder</code> في بداية الشيفرة البرمجية وهو ما لم نحتاجه سابقًا عند استخدامنا لاختبارات الوحدة.
</p>

<p>
	ليس علينا توصّف الشيفرة البرمجية في tests/integration_test.rs باستخدام <code>‎#[cfg(test)]‎</code>، إذ أنّ كارجو تُعامل المجلد "tests" على نحوٍ خاص وتُصرِّف جميع الملفات في هذا المجلد عند تنفيذ الأمر <code>cargo test</code>. ننفّذ <code>cargo test</code> فنحصل على التالي:
</p>

<pre class="ipsCode">$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 1.31s
     Running unittests src/lib.rs (target/debug/deps/adder-1082c4b063a8fbe6)

running 1 test
test tests::internal ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/integration_test.rs (target/debug/deps/integration_test-1082c4b063a8fbe6)

running 1 test
test it_adds_two ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
</pre>

<p>
	تتضمن أقسام الخرج الثلاثة اختبارات الوحدة والتكامل والتوثيق، لاحظ أنه إذا فشل أي اختبار في قسم ما، لن يُنفَّذ القسم التالي. على سبيل المثال، إذا فشل اختبار وحدة فهذا يعني أنه لن يكون هناك أي خرج لاختبار التكامل واختبار التوثيق لأن هذه الاختبارت ستُنفَّذ فقط في حال نجاح جميع اختبارات الوحدة.
</p>

<p>
	القسم الأول هو لاختبارات الوحدة وهو مشابه لما رأيناه سابقًا، إذ يُخصّص كل سطر لاختبار وحدة ما (هناك اختبار واحد يسمى <code>internal</code> أضفناه سابقًا في الشيفرة 12) بالإضافة إلى سطر الملخص لنتائج اختبارات الوحدة.
</p>

<p>
	يبدأ قسم اختبارات التكامل بالسطر <code>Running tests/integration_test.rs</code>، ومن ثم نلاحظ سطرًا لكل دالة اختبار في اختبار التكامل ذلك مع سطر ملخص لنتائج اختبار التكامل قبل بدء القسم <code>Doc-tests adder</code>.
</p>

<p>
	يحتوي كل اختبار تكامل على قسمه الخاص، لذا سنحصل على المزيد من الأقسام إن أضفنا مزيدًا من الملفات في المجلد "tests".
</p>

<p>
	يمكننا تنفيذ دالة اختبار معيّنة بتحديد اسم دالة الاختبار مثل وسيط للأمر <code>cargo test</code>، ولتنفيذ جميع الاختبارات في ملف اختبار تكامل معيّن نستخدم الوسيط <code>‎--test</code> في الأمر <code>cargo test</code> متبوعًا باسم الملف كما يلي:
</p>

<pre class="ipsCode">$ cargo test --test integration_test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.64s
     Running tests/integration_test.rs (target/debug/deps/integration_test-82e7799c1bc62298)

running 1 test
test it_adds_two ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
</pre>

<p>
	ينفّذ هذا الأمر الاختبارات الموجودة في ملف "tests/integration_test.rs" فقط.
</p>

<h3>
	الوحدات الجزئية في اختبارات التكامل
</h3>

<p>
	قد تحتاج إنشاء المزيد من الملفات في المجلد "tests" لمساعدتك في تنظيم اختبارات التكامل في حال إضافتك للمزيد منها، على سبيل المثال يمكنك تجميع <a href="https://academy.hsoub.com/programming/rust/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-functions-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%82%D8%A7%D8%AA-comments-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%A7%D8%B3%D8%AA-rust-r1781/" rel="">دوال الاختبار</a> بناءً على الخاصية التي تفحصها، وكما ذكرنا سابقًا: يُصرَّف كل ملف في المجلد "tests" بمفرده على أنه وحدة مصرّفة، وهو أمر مفيد لإنشاء نطاقات متفرقة عن بعضها بعضًا لمحاكاة الطريقة التي يستخدم فيها المستخدمون وحدتك المصرفة، إلا أن هذا يعني أن الملفات في مجلد "tests" لن تشارك السلوك ذاته الخاص بالملفات في المجلد "src" كما تعلمت سابقًا بخصوص فصل الشيفرة البرمجية إلى وحدات وملفات.
</p>

<p>
	يُلاحظ السلوك بملفات المجلد "tests" بوضوح عندما يكون لديك مجموعةً من الدوال المساعدة تريد استخدامها في ملفات اختبار تكامل مختلفة وتحاول أن تتبع الخطوات الخاصة بفصل الوحدات إلى ملفات مختلفة كما ناقشنا سابقًا لاستخلاصها إلى وحدة مشتركة. على سبيل المثال، إذا أنشأنا "tests/common.rs" ووضعنا دالةً اسمها <code>setup</code> في الملف، يمكننا إضافة شيفرة برمجية إلى <code>setup</code> لاستدعائها من دوال اختبار مختلفة في ملفات اختبار متعددة.
</p>

<p>
	اسم الملف: tests/common.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5060_14" style=""><span class="pln">pub fn setup</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// شيفرة ضبط برمجية مخصصة لاختبارات مكتبتك</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عند تشغيل الاختبارات مجددًا سنرى قسمًا جديدًا في خرج الاختبار للملف "common.rs"، على الرغم من أننا لا نستدعي الدالة <code>setup</code> من أي مكان كما أن هذا الملف لا يحتوي على أي دوال اختبار:
</p>

<pre class="ipsCode">$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.89s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 1 test
test tests::internal ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/common.rs (target/debug/deps/common-92948b65e88960b4)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/integration_test.rs (target/debug/deps/integration_test-92948b65e88960b4)

running 1 test
test it_adds_two ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
</pre>

<p>
	العثور على <code>common</code> في نتائج الاختبار مع <code>running 0 tests</code> ليس ما أردنا رؤيته، إذا أننا أردنا مشاركة جزء من شيفرة برمجية مع ملفات اختبار التكامل الأخرى.
</p>

<p>
	لتجنب الحصول على <code>common</code> في خرج الاختبار، نُنشئ "tests/common/mod.rs" بدلًا من "tests/common.rs"، بحيث يبدو هيكل مجلد المشروع كما يلي:
</p>

<pre class="ipsCode">├── Cargo.lock
├── Cargo.toml
├── src
│   └── lib.rs
└── tests
    ├── common
    │   └── mod.rs
    └── integration_test.rs
</pre>

<p>
	هذا هو اصطلاح التسمية القديم في رست وقد ذكرناه سابقًا في فصل <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AD%D8%B2%D9%85-packages-%D9%88%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B5%D8%B1%D9%81%D8%A9-crates-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1853/" rel="">الحزم والوحدات</a>، إذ تخبر تسمية الملف بهذا الاسم رست بعدم التعامل مع الوحدة <code>common</code> على أنها ملف اختبار تكامل، وبالتالي لن يظهر هذا القسم في خرج الاختبار بعد أن ننقل شيفرة الدالة البرمجية <code>setup</code> إلى "tests/common/mod.rs" ونحذف الملف "tests/common.rs". لا تُصرَّف الملفات الموجودة في المجلدات الفرعية في المجلد tests مثل وحدات مصرّفة متفرقة أو تحتوي على أقسام متفرقة في خرج الاختبار.
</p>

<p>
	يمكننا استخدام أي من ملفات اختبار التكامل مثل وحدة بعد إنشاء الملف "tests/common/mod.rs"، إليك مثالًا على استدعاء الدالة <code>setup</code> من الاختبار <code>it_adds_two</code> في "tests/integration_test.rs":
</p>

<p>
	اسم الملف: tests/integration_tests.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5060_16" style=""><span class="pln">use adder</span><span class="pun">;</span><span class="pln">

mod common</span><span class="pun">;</span><span class="pln">

</span><span class="com">#[test]</span><span class="pln">
fn it_adds_two</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    common</span><span class="pun">::</span><span class="pln">setup</span><span class="pun">();</span><span class="pln">
    assert_eq</span><span class="pun">!(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> adder</span><span class="pun">::</span><span class="pln">add_two</span><span class="pun">(</span><span class="lit">2</span><span class="pun">));</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لاحظ أن التصريح <code>mod common;‎</code> مماثلٌ لتصريح الوحدة التي شرحنا عنها سابقًا في الشيفرة 21 فصل <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%B3%D8%A7%D8%B1%D8%A7%D8%AA-paths-%D9%88%D8%A7%D9%84%D9%86%D8%B7%D8%A7%D9%82-%D8%A7%D9%84%D8%AE%D8%A7%D8%B5-%D8%A8%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1874/" rel="">المسارات paths والنطاق الخاص بها في لغة رست Rust</a>. يمكننا بعد ذلك استدعاء الدالة <code>common::setup()‎</code> من دالة الاختبار.
</p>

<h3>
	اختبارات التكامل للوحدات الثنائية المصرفة
</h3>

<p>
	لا يمكننا إنشاء اختبارات تكامل في المجلد "tests" وإضافة الدوال المعرفة في الملف "src/main.rs" إلى النطاق باستخدام تعليمة <code>use</code> إذا كان مشروعنا يمثّل وحدة ثنائية مصرفة binary crate تحتوي على ملف "src/main.rs" فقط ولا تحتوي على ملف "src/lib.rs"، إذ أن وحدات المكتبة المصرفة وحدها قادرة على كشف الدوال التي يمكن للوحدات المصرّفة الأخرى استخدامها؛ لأن الهدف من الوحدات الثنائية المصرفة هو تنفيذها بصورةٍ مستقلة.
</p>

<p>
	هذا واحدٌ من الأسباب لكون مشاريع رست التي تدعم ملفات ثنائية تأتي بملف "src/main.rs"، الذي يستدعي المنطق البرمجي الموجود في الملف src/lib.rs. يمكن لاختبارات التكامل باستخدام هذا التنظيم بأن تختبر صندوق المكتبة المصرف باستخدام <code>use</code> لجعل الدوال المهمة متاحة، فإذا عملت الوظيفة الأساسية، عنى ذلك أن الأجزاء الصغيرة من الشيفرة البرمجية في الملف src/main.rs ستعمل أيضًا ويجب اختبار هذه الأجزاء الصغيرة.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch11-00-testing.html" rel="external nofollow">Writing Automated Tests</a> من كتاب <a href="https://doc.rust-lang.org/stable/book/title-page.html/" rel="external nofollow">The Rust Programming Language</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%B3%D8%B7%D8%B1-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-command-line-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AF%D8%AE%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%B1%D8%AC-r1969/" rel="">كتابة برنامج سطر أوامر Command Line بلغة رست Rust: التعامل مع الدخل والخرج</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A8%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1960/" rel="">التحكم بتنفيذ الاختبارات في لغة رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%B3%D8%A7%D8%B1%D8%A7%D8%AA-paths-%D9%88%D8%B4%D8%AC%D8%B1%D8%A9-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A9-module-tree-%D9%81%D9%8A-%D8%B1%D8%B3%D8%AA-rust-r1873/" rel="">المسارات paths وشجرة الوحدة module tree في رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D9%8A%D8%A7%D8%B1-%D9%85%D8%A7-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%A7%D9%83%D8%B1%D9%88-panic%E2%80%8E-%D9%88%D8%A7%D9%84%D9%86%D9%88%D8%B9-result-%D9%84%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-rust-r1921/" rel="">الاختيار ما بين الماكرو panic!‎ والنوع Result للتعامل مع الأخطاء في لغة Rust</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1961</guid><pubDate>Sun, 30 Apr 2023 13:08:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x62D;&#x643;&#x645; &#x628;&#x62A;&#x646;&#x641;&#x64A;&#x630; &#x627;&#x644;&#x627;&#x62E;&#x62A;&#x628;&#x627;&#x631;&#x627;&#x62A; &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A; Rust</title><link>https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A8%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1960/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_04/---Tests----Rust.jpg.108576a9d2f0eb0fe4974041b727d4c4.jpg" /></p>
<p>
	يصرّف <code>cargo test</code> شيفرتك البرمجية بالطريفة نفسها التي يصرّف فيها الأمر <code>cargo run</code> شيفرتك البرمجية ويشغّلها، إلا أن <code>cargo test</code> يصرّف الشيفرة البرمجية في نمط الاختبار ويشغّل ملف الاختبار الثنائي. السلوك الافتراضي للملف الثنائي الناتج عن <code>cargo test</code> هو تشغيل جميع الاختبارات على التوازي والحصول على الخرج خلال تشغيل الاختبار ومنع الخرج من العرض مما يجعل من الخرج المتعلق بنتائج الاختبار أكثر وضوحًا، إلا أنه يمكنك كتابة خيارات في سطر الأوامر للتغيير من هذا السلوك الافتراضي.
</p>

<p>
	تنتمي بعض الخيارات في <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر الأوامر</a> إلى <code>cargo test</code> بينما ينتمي بعضها لملف الاختبار الثنائي الناتج، وللفصل بين النوعين من الوسطاء نضع الوسطاء الخاصة بالأمر <code>cargo test</code> متبوعةً بالفاصل <code>--</code> ومن ثم الوسطاء الخاصة بملف الاختبار الثنائي. يعرض تنفيذ الأمر <code>cargo test --help</code> الخيارات الممكن استخدامها مع <code>cargo test</code>، بينما يعرض تنفيذ <code>cargo test -- --help</code> الخيارات التي يمكنك استخدامها بعد الفاصل.
</p>

<h2>
	تشغيل الاختبارات على نحو متعاقب أو على التوازي
</h2>

<p>
	تُنفّذ الاختبارات على التوازي افتراضيًا عند تشغيل عدة اختبارات باستخدام خيوط threads، مما يعني أن تنفيذها سيكون سريعًا وستحصل على نتيجتك بصورةٍ أسرع، وبما أن الاختبارات تُنفّذ في الوقت ذاته فهذا يعني أنها يجب ألا تعتمد على بعضها بعضًا أو تحتوي على حالة مشتركة بما فيه بيئة مشتركة مثل مسار العمل الحالي current working directory أو متغيرات البيئة environment variables.
</p>

<p>
	على سبيل المثال لنقل أن اختباراتك تنفذ شيفرة برمجية تُنشئ ملفًا على قرص باسم test-output.txt وتكتب بعض البيانات على هذا الملف، يقرأ عندها كل اختبار البيانات في ذلك الملف ويتأكد أن الملف يحتوي على قيمة معينة وهي قيمة مختلفة بحسب كل اختبار، ولأن الاختبارات تُشغّل في الوقت ذاته فهذا يعني أن أحد الاختبارات قد يكتب على محتويات الملف في وقت تنفيذ اختبار آخر وقراءته للملف وسيفشل عندها الاختبار الثاني ليس لعدم صحة الشيفرة البرمجية بل لأن الاختبارات أثرت على بعضها بعضًا عند تشغيلها على التوازي. يكمن أحد الحلول هنا بالتأكد أن كل اختبار يكتب إلى ملف مختلف، وهناك حلّ آخر يتمثل بتشغيل الاختبارات على التتالي كلّ حسب دوره.
</p>

<p>
	إذا لم ترد تشغيل الاختبارات على التوازي، أو أردت تحكمًا أكبر على أرقام <a href="https://academy.hsoub.com/programming/java/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1483/" rel="">الخيوط</a> التي تريد استخدامها فيمكنك عندئذ استخدام الراية <code>‎--test-threads</code> متبوعةً بعدد الخيوط التي تريد استخدامها مع الاختبار الثنائي. ألقِ نظرةً على المثال التالي:
</p>

<pre class="ipsCode">$ cargo test -- --test-threads=1
</pre>

<p>
	نضبط عدد خيوط الاختبار إلى "1"، وهذا يجعل البرنامج يعرف أننا لا نريد تشغيل الاختبارات على التوازي، إذ يستغرق تشغيل الاختبارات باستخدام خيط واحد وقتًا أكبر من تشغيلها على التوازي، إلا أن الاختبارات لن تتداخل في عمل بعضها بعضًا إذا تشاركت في حالة ما.
</p>

<h2>
	عرض خرج الدالة
</h2>

<p>
	تلتقط مكتبة اختبار رست تلقائيًا كل شيء يُطبع إلى الخرج القياسي إذا نجح الاختبار، على سبيل المثال إذا استدعينا <code>println!‎</code> في اختبار ما ونجح هذا الاختبار فلن نرى خرج <code>println!‎</code> في الطرفية بل سنرى فقط السطر الذي يشير إلى نجاح الاختبار، بينما سنرى ما طُبع إلى الخرج القياسي إذا فشل الاختبار مصحوبًا مع رسالة الفشل.
</p>

<p>
	تحتوي الشيفرة 10 على مثال بسيط يحتوي على دالة تطبع قيمة معاملها وتُعيد 10 إضافةً إلى اختبار ينجح وآخر يفشل.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4287_8" style=""><span class="pln">fn prints_and_returns_10</span><span class="pun">(</span><span class="pln">a</span><span class="pun">:</span><span class="pln"> i32</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> i32 </span><span class="pun">{</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"I got the value {}"</span><span class="pun">,</span><span class="pln"> a</span><span class="pun">);</span><span class="pln">
    </span><span class="lit">10</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">#[cfg(test)]</span><span class="pln">
mod tests </span><span class="pun">{</span><span class="pln">
    use super</span><span class="pun">::*;</span><span class="pln">

    </span><span class="com">#[test]</span><span class="pln">
    fn this_test_will_pass</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let value </span><span class="pun">=</span><span class="pln"> prints_and_returns_10</span><span class="pun">(</span><span class="lit">4</span><span class="pun">);</span><span class="pln">
        assert_eq</span><span class="pun">!(</span><span class="lit">10</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="com">#[test]</span><span class="pln">
    fn this_test_will_fail</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let value </span><span class="pun">=</span><span class="pln"> prints_and_returns_10</span><span class="pun">(</span><span class="lit">8</span><span class="pun">);</span><span class="pln">
        assert_eq</span><span class="pun">!(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 10: اختبارات للدالة التي تستدعي <code>println!‎</code>]
</p>

<p>
	نحصل على الخرج التالي عند تشغيل هذه الاختبارات باستخدام <code>cargo test</code>:
</p>

<pre class="ipsCode">$ cargo test
   Compiling silly-function v0.1.0 (file:///projects/silly-function)
    Finished test [unoptimized + debuginfo] target(s) in 0.58s
     Running unittests src/lib.rs (target/debug/deps/silly_function-160869f38cff9166)

running 2 tests
test tests::this_test_will_fail ... FAILED
test tests::this_test_will_pass ... ok

failures:

---- tests::this_test_will_fail stdout ----
I got the value 8
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `5`,
 right: `10`', src/lib.rs:19:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::this_test_will_fail

test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass '--lib'
</pre>

<p>
	لاحظ أن الخرج قد التُقط ولا يوجد فيه <code>I got the value 4</code> وهو ما نطبعه عند تشغيل الاختبار الذي سينجح، بينما يظهر الخرج <code>I got the value 8</code> من الاختبار الذي فشل في قسم خرج ملخص الاختبار الذي يوضح أيضًا سبب فشل الاختبار.
</p>

<p>
	يمكننا اخبار رست بعرض خرج الاختبارات الناجحة باستخدام <code>‎--show-output</code> إذا أردنا رؤية القيم المطبوعة للاختبارات الناجحة أيضًا.
</p>

<pre class="ipsCode">$ cargo test -- --show-output
</pre>

<p>
	نحصل على الخرج التالي عند تشغيل الاختبارات الموجودة في الشيفرة 10 مجددًا باستخدام الراية <code>‎--show-output</code>:
</p>

<pre class="ipsCode">$ cargo test -- --show-output
   Compiling silly-function v0.1.0 (file:///projects/silly-function)
    Finished test [unoptimized + debuginfo] target(s) in 0.60s
     Running unittests src/lib.rs (target/debug/deps/silly_function-160869f38cff9166)

running 2 tests
test tests::this_test_will_fail ... FAILED
test tests::this_test_will_pass ... ok

successes:

---- tests::this_test_will_pass stdout ----
I got the value 4


successes:
    tests::this_test_will_pass

failures:

---- tests::this_test_will_fail stdout ----
I got the value 8
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `5`,
 right: `10`', src/lib.rs:19:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::this_test_will_fail

test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass '--lib'
</pre>

<h2>
	تشغيل مجموعة من الاختبارات باستخدام اسم
</h2>

<p>
	قد يستغرق تشغيل كافة الاختبارات في بعض الأحيان وقتًا طويلًا، وقد تريد تشغيل مجموعة من الاختبارات مرتبطة فقط بجزئية معينة ضمن شيفرتك البرمجية، ويمكنك اختيار الاختبارات التي تريد تنفيذها بتمرير اسم الاختبار أو أسماء الاختبارات مثل وسطاء إلى الأمر <code>cargo test</code>.
</p>

<p>
	لتوضيح كيفية تشغيل مجموعة من الاختبارات نُنشئ أولًا ثلاث اختبارات للدالة <code>add_two</code> كما هو موضح في الشيفرة 11 ونختار أي الاختبارات التي نريد تشغيلها.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4287_11" style=""><span class="pln">pub fn add_two</span><span class="pun">(</span><span class="pln">a</span><span class="pun">:</span><span class="pln"> i32</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> i32 </span><span class="pun">{</span><span class="pln">
    a </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">#[cfg(test)]</span><span class="pln">
mod tests </span><span class="pun">{</span><span class="pln">
    use super</span><span class="pun">::*;</span><span class="pln">

    </span><span class="com">#[test]</span><span class="pln">
    fn add_two_and_two</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        assert_eq</span><span class="pun">!(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> add_two</span><span class="pun">(</span><span class="lit">2</span><span class="pun">));</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="com">#[test]</span><span class="pln">
    fn add_three_and_two</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        assert_eq</span><span class="pun">!(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> add_two</span><span class="pun">(</span><span class="lit">3</span><span class="pun">));</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="com">#[test]</span><span class="pln">
    fn one_hundred</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        assert_eq</span><span class="pun">!(</span><span class="lit">102</span><span class="pun">,</span><span class="pln"> add_two</span><span class="pun">(</span><span class="lit">100</span><span class="pun">));</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 11: ثلاثة اختبارات مع ثلاثة أسماء مختلفة]
</p>

<p>
	ستُنفّذ جميع الاختبارات على التوازي إذا شغّلنا الاختبارات دون تمرير أي وسطاء كما فعلنا سابقًا:
</p>

<pre class="ipsCode">$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.62s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 3 tests
test tests::add_three_and_two ... ok
test tests::add_two_and_two ... ok
test tests::one_hundred ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
</pre>

<h3>
	تشغيل الاختبارات بصورة فردية
</h3>

<p>
	يمكننا تمرير اسم أي دالة اختبار للأمر <code>cargo test</code> لتنفيذ الاختبار بصورةٍ فردية:
</p>

<pre class="ipsCode">$ cargo test one_hundred
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.69s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 1 test
test tests::one_hundred ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out; finished in 0.00s
</pre>

<p>
	جرى تشغيل الاختبار بالاسم <code>one_hundred</code> فقط، إذ لم يطابق الاختباران الآخران هذا الاسم. يسمح لنا الخرج بمعرفة أن هناك المزيد من الاختبارات التي لم ننفذها بعرض <code>2 filtered out</code> في النهاية.
</p>

<p>
	لا يمكننا تحديد أسماء اختبارات متعددة بهذه الطريقة، إذ تُستخدم القيمة الأولى المُعطاة للأمر <code>cargo test</code> فقط، إلا أن هناك وسيلة أخرى لتنفيذ عدة اختبارات.
</p>

<h2>
	تنفيذ عدة اختبارات عن طريق الترشيح
</h2>

<p>
	يمكننا تحديد جزء من اسم اختبار بحيث يُنفّذ أي اختبار يطابق اسمه هذه القيمة. على سبيل المثال، يمكننا تنفيذ اختبارين من الاختبارات الثلاثة السابقة عن طريق <code>add</code> وذلك بكتابة الأمر <code>cargo test add</code>:
</p>

<pre class="ipsCode">$ cargo test add
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.61s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 2 tests
test tests::add_three_and_two ... ok
test tests::add_two_and_two ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; finished in 0.00s
</pre>

<p>
	يشغّل هذا الأمر جميع الاختبارات التي تحتوي على <code>add</code> في اسمها، ويستثني هنا الاختبار <code>one_hundred</code>، كما يجب ملاحظة أن الوحدة التي تحتوي على الاختبار بداخلها تصبح جزءًا من اسم الاختبار، أي يمكننا تشغيل جميع الاختبارات الموجودة في وحدة معينة عن طريق استخدام اسمها.
</p>

<h2>
	تجاهل بعض الاختبارات إلا في حال طلبها
</h2>

<p>
	قد يكون لدينا في بعض الأحيان اختبارات معينة تستغرق وقتًا طويلًا لتنفيذها وقد ترغب باستثنائها من التشغيل عند كتابة الأمر <code>cargo test</code>. يمكنك هنا استخدام توصيف الاختبارات التي تستغرق وقتًا طويلًا باستخدام السمة <code>ignore</code> لاستثنائها بدلًا من كتابة جميع الاختبارات التي تريد تشغيلها مثل وسطاء باستثناء تلك الاختبارات، كما هو موضح هنا:
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4287_13" style=""><span class="com">#[test]</span><span class="pln">
fn it_works</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    assert_eq</span><span class="pun">!(</span><span class="lit">2</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">#[test]</span><span class="pln">
</span><span class="com">#[ignore]</span><span class="pln">
fn expensive_test</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// شيفرة برمجية تستغرق عدة ساعات للتنفيذ</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نضيف <code>‎#[ignore]‎</code> بعد سطر <code>‎#[test]‎</code> ضمن الاختبار الذي نريد استثناءه، والآن عند تشغيل الاختبارات يُنفّذ الاختبار <code>it_works</code> دون <code>expensive_test</code>:
</p>

<pre class="ipsCode">$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.60s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 2 tests
test expensive_test ... ignored
test it_works ... ok

test result: ok. 1 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
</pre>

<p>
	تُدرج الدالة <code>expensive_test</code> تحت <code>ignored</code>، وإذا أردنا تشغيل الاختبارات التي تجاهلناها فقط نكتب <code>cargo test -- --ignored</code>:
</p>

<pre class="ipsCode">$ cargo test -- --ignored
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.61s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 1 test
test expensive_test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
</pre>

<p>
	نتأكد من خلال التحكم بالاختبارات التي تُنفَّذ من أن نتائج <code>cargo test</code> ستكون سريعة، يمكنك تنفيذ <code>cargo test -- --ignored</code> عندما تكون في حالة تريد فيها التحقق من الاختبارات التي تندرج تحت <code>ignored</code> ولديك الوقت لانتظار النتائج، بينما تستطيع تنفيذ الأمر التالي إذا أردت تشغيل جميع الاختبارات المتجاهلة وغير المتجاهلة دفعةً واحدة.
</p>

<pre class="ipsCode">cargo test -- --include-ignored
</pre>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch11-00-testing.html" rel="external nofollow">Writing Automated Tests</a> من كتاب <a href="https://doc.rust-lang.org/stable/book/title-page.html/" rel="external nofollow">The Rust Programming Language</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D9%86%D8%B8%D9%8A%D9%85-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-tests-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1961/" rel="">تنظيم الاختبارات Tests في لغة رست Rust</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1959/" rel="">كتابة الاختبارات في لغة رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-functions-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%82%D8%A7%D8%AA-comments-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%A7%D8%B3%D8%AA-rust-r1781/" rel="">كيفية كتابة الدوال Functions والتعليقات Comments في لغة راست Rust</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1960</guid><pubDate>Wed, 26 Apr 2023 13:00:00 +0000</pubDate></item><item><title>&#x643;&#x62A;&#x627;&#x628;&#x629; &#x627;&#x644;&#x627;&#x62E;&#x62A;&#x628;&#x627;&#x631;&#x627;&#x62A; &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A; Rust</title><link>https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1959/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_04/--Tests----Rust.jpg.200a8b5de34ae03d1641ca90e5e44580.jpg" /></p>
<p>
	الاختبارات Tests هي مجموعة من دوال رست تتأكد من أن الشيفرة البرمجية الأساسية تعمل كما هو مطلوب منها، ويؤدي متن دوال الاختبار عادةً هذه العمليات الثلاث:
</p>

<ol>
	<li>
		تجهيز أي بيانات أو حالة ضرورية.
	</li>
	<li>
		تنفيذ الشيفرة البرمجية التي تريد اختبارها.
	</li>
	<li>
		التأكد من أن النتائج وفق المتوقع.
	</li>
</ol>

<p>
	لننظر إلى المزايا التي توفرها رست لكتابة الاختبارات التي تؤدي العمليات الثلاث السابقة، ويتضمن ذلك السمة <code>test</code> وبعض الماكرو والسمة <code>should_panic</code>.
</p>

<h2>
	بنية دالة الاختبار
</h2>

<p>
	‎ تُوصَّف دالة <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%84%D9%84%D8%AA%D9%88%D8%A7%D9%81%D9%82-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-r1953/" rel="">الاختبار</a> في رست باستخدام السمة <code>test</code>؛ و<a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%B3%D9%85%D8%A7%D8%AA-traits-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1951/" rel="">السمات attributes</a> هي بيانات وصفية metadata تصف أجزاءً من شيفرة رست البرمجية، ومثال على هذه السمات هي السمة <code>derive</code> التي استخدمناها مع الهياكل سابقًا. لتغيير دالة عادية إلى دالة اختبار نُضيف <code>‎#[test]‎</code> قبل السطر الذي نكتب فيه <code>fn</code>، إذ تبني رست ملف تنفيذ اختبار ثنائي عند تنفيذ الاختبارات باستخدام الأمر <code>cargo test</code>، وتختبر <a href="https://academy.hsoub.com/programming/rust/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-functions-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%82%D8%A7%D8%AA-comments-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%A7%D8%B3%D8%AA-rust-r1781/" rel="">الدوال</a> المُشار إليها بأنها دوال اختبار وتعرض لك تقريرًا يوضح أي الدوال التي فشلت وأيها التي نجحت.
</p>

<p>
	تُولَّد وحدة اختبار test module مع دالة اختبار تلقائيًا عندما نُنشئ مشروع مكتبة جديدة باستخدام كارجو Cargo، وتمنحنا هذه الوحدة قالبًا لكتابة الاختبارات التي نريد، بحيث لا يتوجب عليك النظر إلى هيكل الاختبار وطريقة كتابته كل مرة تُنشئ فيها مشروعًا جديدًا، ويمكنك إضافة دوال اختبار ووحدات اختبار إضافية قدر ما تشاء.
</p>

<p>
	سننظر سويًا إلى بعض جوانب عمل الاختبارات بتجربة قالب الاختبار قبل اختبار أي شيفرة برمجية فعليًا، ثم سنكتب اختبارات واقعية تستدعي شيفرةً برمجيةً كتبناها لاحقًا وتتأكد من صحة سلوكها.
</p>

<p>
	ننشئ مشروع مكتبة جديدة نسميه <code>adder</code> يضيف رقمين إلى بعضهما:
</p>

<pre class="ipsCode">$ cargo new adder --lib
     Created library `adder` project
$ cd adder
</pre>

<p>
	يجب أن تبدو محتويات الملف src/lib.rs في مكتبة <code>adder</code> كما هو موضح في الشيفرة 1.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8722_9" style=""><span class="com">#[cfg(test)]</span><span class="pln">
mod tests </span><span class="pun">{</span><span class="pln">
    </span><span class="com">#[test]</span><span class="pln">
    fn it_works</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let result </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
        assert_eq</span><span class="pun">!(</span><span class="pln">result</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 1: وحدة الاختبار والدالة المولّدة تلقائيًا باستخدام <code>cargo new</code>
</p>

<p>
	لنتجاهل أول سطرين ونركّز على الدالةحاليًا. لاحظ التوصيف <code>‎#[test]‎</code>: تُشير هذه السمة إلى أن هذه دالة اختبار، ما يعني أن منفّذ الاختبار سيعامل هذه الدالة على أنها اختبار، وقد تحتوي شيفرتنا البرمجية أيضًا على دوال ليست بدوال اختبار في وحدة <code>tests</code> وذلك بهدف مساعدتنا لضبط حالات معينة، أو إجراء عمليات شائعة، لذا نحدّد دومًا فيما إذا كانت الدالة دالة اختبار.
</p>

<p>
	يُستخدم متن الدالة في المثال الماكرو <code>assert_eq!‎</code> للتأكد من أن <code>result</code> تحتوي على القيمة 4 (وهي تحتوي على نتيجة جمع الرقم 2 مع 2). تمثّل عملية التأكد هذه عملية اختبارًا تقليديًا، دعنا ننفذ الاختبار لنرى إذا ما كان سينجح أم لا.
</p>

<p>
	ينفِّذ الأمر <code>cargo test</code> جميع الاختبارات الموجودة في المشروع كما هو موضح في الشيفرة 2.
</p>

<pre class="ipsCode">$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.57s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
</pre>

<p style="text-align: center;">
	الشيفرة 2: الخرج الناتج عن عملية تنفيذ الاختبارات المولّدة تلقائيًا
</p>

<p>
	يُصرّف كارجو الاختبار ويشغّله؛ إذ نجد في أحد السطور <code>running 1 test</code>، ثم سطر يليه يوضح اسم دالة الاختبار المولّدة التي تدعى <code>it_works</code> وأن نتيجة ذلك الاختبار هي <code>ok</code>، وتعني النتيجة النهائية <code>test result: ok.‎</code> أن جميع الاختبارات نجحت، بينما يشير الجزء <code>1 passed; 0 failed</code> إلى عدد الاختبارات الناجحة وعدد الاختبارات الفاشلة.
</p>

<p>
	من الممكن تجاهل الاختبار بحيث لا يُنفّذ في حالات معينة وسنتكلم عن هذا الأمر لاحقًا، إلا أن ملخص نتيجة الاختبارات يوضح <code>‎0 ignored</code> لأننا لم نفعل ذلك هنا، كما يمكننا تمرير وسيط إلى الأمر <code>cargo test</code> بحيث ينفذ الاختبارات التي يوافق اسمها السلسلة النصية ويدعى هذا بالترشيح filtering وسنتكلم عن هذا الموضوع لاحقًا. تظهر نهاية الملخص <code>‎0 filtered out</code> لأننا لم نستخدم الترشيح على الاختبارات التي ستُنفّذ.
</p>

<p>
	يشير <code>‎0 measured</code> إلى الاختبارات المعيارية التي تقيس الأداء، إذ أن الاختبارات المعيارية benchmark tests متاحةٌ فقط في رست الليلية nightly Rust -في وقت كتابة هذه الكلمات- ويمكنك النظر إلى <a href="https://doc.rust-lang.org/stable/unstable-book/library-features/test.html" rel="external nofollow">التوثيق المتعلق بالاختبارات المعيارية</a> لتعلُّم المزيد.
</p>

<p>
	يبدأ الجزء التالي من خرج الاختبار بالجملة <code>Doc-tests adder</code> وهو نتيجة لأي من اختبارات التوثيق، إلا أنه لا توجد لدينا أي اختبارات توثيق حاليًا، لكن يمكن لرست تصريف أي مثال شيفرة برمجية موجودة في توثيق <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">واجهتنا البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a>، وتساعدنا هذه الميزة بالمحافظة على التوثيق وشيفرتنا البرمجية على نحوٍ متوافق. سنناقش كيفية كتابة اختبارات التوثيق لاحقًا، وسنتجاهل قسم الخرج <code>Doc-tests</code> حاليًا.
</p>

<p>
	لنبدأ بتخصيص الاختبار ليوافق حاجتنا؛ إذ سنغيّر أولًا اسم الدالة <code>it_works</code> إلى اسم مختلف مثل <code>exploration</code> كما يلي:
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8722_12" style=""><span class="com">#[cfg(test)]</span><span class="pln">
mod tests </span><span class="pun">{</span><span class="pln">
    </span><span class="com">#[test]</span><span class="pln">
    fn exploration</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        assert_eq</span><span class="pun">!(</span><span class="lit">2</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نشغِّل <code>cargo test</code> مجددًا. يعرض لنا الخرج الآن <code>exploration</code> بدلًا من <code>it_works</code>:
</p>

<pre class="ipsCode">$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.59s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 1 test
test tests::exploration ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
</pre>

<p>
	نضيف الآن اختبارًا جديدًا، إلا أننا سنجعل هذا الاختبار يفشل عمدًا، إذ تفشل الاختبارات عندما يهلع panic شيءٌ ما داخل دالة الاختبار. يُجرى كل اختبار ضمن خيط thread جديد وعندما يرى الخيط الرئيس أن خيط الاختبار قد "انتهى" يُعَلَّم الاختبار بأنه فشل.
</p>

<p>
	تكلمنا <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1920/" rel="">سابقًا</a> عن طرق أبسط لهلع الشيفرة البرمجية باستخدام <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D9%8A%D8%A7%D8%B1-%D9%85%D8%A7-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%A7%D9%83%D8%B1%D9%88-panic%E2%80%8E-%D9%88%D8%A7%D9%84%D9%86%D9%88%D8%B9-result-%D9%84%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-rust-r1921/" rel="">الماكرو panic!</a><code>‎</code>. الآن، نُدخل الاختبار الجديد مثل دالة تسمى <code>another</code> إلى الملف src/lib.rs كما هو موضح في الشيفرة 3.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8722_14" style=""><span class="com">#[cfg(test)]</span><span class="pln">
mod tests </span><span class="pun">{</span><span class="pln">
    </span><span class="com">#[test]</span><span class="pln">
    fn exploration</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        assert_eq</span><span class="pun">!(</span><span class="lit">2</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="com">#[test]</span><span class="pln">
    fn another</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        panic</span><span class="pun">!(</span><span class="str">"Make this test fail"</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 3: إضافة اختبار جديد يفشل لأننا نستدعي الماكرو <code>panic!‎</code>
</p>

<p>
	شغِّل الاختبارات مجددًا باستخدام <code>cargo test</code>، يجب أن يكون الخرج مشابهًا لما هو موجود في الشيفرة 4، وهو يوضّح أن اختبار <code>exploration</code> نجح بينما فشل <code>another</code>.
</p>

<pre class="ipsCode">$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.72s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 2 tests
test tests::another ... FAILED
test tests::exploration ... ok

failures:

---- tests::another stdout ----
thread 'main' panicked at 'Make this test fail', src/lib.rs:10:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::another

test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass '--lib'
</pre>

<p style="text-align: center;">
	الشيفرة 4: نتائج الاختبارات، إذ نجح اختبار وفشل آخر
</p>

<p>
	يعرض السطر <code>test tests::another</code> النتيجة <code>FAILED</code> بدلًا من <code>ok</code>، ويظهر لنا قسمين جديدين بين النتائج الفردية والملخص: إذ يعرض الأول السبب لفشل كل من الاختبارات بالتفصيل، وفي هذه الحالة نحصل على التفاصيل الخاصة بفشل <code>another</code> وهي أن <code>panicked at 'Make this test fail'‎</code> في السطر 10 ضمن الملف src/lib.rs؛ بينما يعرض القسم التالي أسماء جميع الاختبارات التي فشلت، وهي معلومة مفيدة في حال وجد لدينا العديد من الاختبارات مع العديد من التفاصيل لكل اختبار فشل. يمكن استخدام اسم الاختبار الذي فشل لتشغيل الاختبار وحده والحصول على معلومات أدق لتنقيح الأخطاء، وسنتكلم عن طرق تشغيل الاختبارات لاحقًا.
</p>

<p>
	يعرض سطر الملخص في النهاية نتيجة الاختبارات كاملةً، إذ أن نتيجة الاختبار هي <code>FAILED</code> ووجِد لدينا اختبارٌ نجح وآخر فشل.
</p>

<p>
	الآن بعد أن تعرفنا إلى كيفية عرض نتائج الاختبار في حالات مختلفة، ننظر إلى ماكرو مختلفة عن <code>panic!‎</code> مفيدة في الاختبارات.
</p>

<h2>
	التحقق من النتائج باستخدام الماكرو assert!‎
</h2>

<p>
	تُعد الماكرو <code>assert!‎</code> الموجودة في المكتبة القياسية مفيدةً عندما تريد التأكد من أن شرطًا ما ضمن الاختبار يُقيَّم إلى <code>true</code>، ونمرّر للماكرو <code>assert!‎</code> وسيطًا يمكن تقييمه لقيمة بوليانية boolean؛ فإذا كانت القيمة <code>true</code> لا يحدث شيء عندها ونجتاز الاختبار بنجاح؛ وإذا حصلنا على القيمة <code>false</code> فهذا يعني فشل الاختبار، ويستدعي الماكرو <code>assert!‎</code> عندها <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1920/" rel="">الماكرو <code>panic!‎</code></a>. يساعدنا استخدام الماكرو <code>assert!‎</code> في التحقق من شيفرتنا البرمجية بالطريقة التي نريدها.
</p>

<p>
	استخدمنا في الأمثلة البرمجية سابقًا هيكلًا يدعى <code>Rectangle</code> وتابع <code>can_hold</code>، وتجد المثال مكررًا هنا في الشيفرة 5. دعنا نضع هذه الشيفرة البرمجية في الملف src/lib.rs ومن ثم نكتب بعض الاختبارات باستخدام الماكرو <code>assert!‎</code>.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8722_16" style=""><span class="com">#[derive(Debug)]</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    width</span><span class="pun">:</span><span class="pln"> u32</span><span class="pun">,</span><span class="pln">
    height</span><span class="pun">:</span><span class="pln"> u32</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

impl </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn can_hold</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> other</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="typ">Rectangle</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="kwd">bool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">width </span><span class="pun">&gt;</span><span class="pln"> other</span><span class="pun">.</span><span class="pln">width </span><span class="pun">&amp;&amp;</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">height </span><span class="pun">&gt;</span><span class="pln"> other</span><span class="pun">.</span><span class="pln">height
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 5: استخدام الهيكل <code>Rectangle</code> وتابعه <code>can_hold</code> من مثال سابق
</p>

<p>
	يمكن أن يُعيد التابع <code>can_hold</code> قيمةً بوليانية، وهذا يعني أننا نستطيع استخدامه مع الماكرو <code>assert!‎</code>. سنكتب اختبارًا لنتدرّب على التابع <code>can_hold</code> بإنشاء نسخة <code>Rectangle</code> في الشيفرة 6، وتحمل النسخة عرضًا بمقدار 8 وطولًا بمقدار 7، ومن ثم نتأكد من أنها تستطيع حمل hold نسخة <code>Rectangle</code> أخرى بعرض 5 وطول 1.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8722_18" style=""><span class="com">#[cfg(test)]</span><span class="pln">
mod tests </span><span class="pun">{</span><span class="pln">
    use super</span><span class="pun">::*;</span><span class="pln">

    </span><span class="com">#[test]</span><span class="pln">
    fn larger_can_hold_smaller</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let larger </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">8</span><span class="pun">,</span><span class="pln">
            height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">};</span><span class="pln">
        let smaller </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln">
            height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">};</span><span class="pln">

        assert</span><span class="pun">!(</span><span class="pln">larger</span><span class="pun">.</span><span class="pln">can_hold</span><span class="pun">(&amp;</span><span class="pln">smaller</span><span class="pun">));</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 6: اختبار للتابع <code>can_hold</code> يتحقق فيما إذا كان المستطيل الكبير يتسع مستطيلًا أصغر
</p>

<p>
	لاحظ أننا أضفنا سطرًا جديدًا داخل وحدة <code>tests</code> ألا وهو <code>use super::*;‎</code>، إذ تمثّل وحدة <code>test</code> وحدةً اعتيادية تتبع قواعد الظهورالاعتيادية visibility rules التي ناقشناها سابقًا في <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%B3%D8%A7%D8%B1%D8%A7%D8%AA-paths-%D9%88%D8%B4%D8%AC%D8%B1%D8%A9-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A9-module-tree-%D9%81%D9%8A-%D8%B1%D8%B3%D8%AA-rust-r1873/" rel="">مقال المسارات paths وشجرة الوحدة module tree في رست Rust</a>، ولأن وحدة <code>tests</code> هي وحدة داخلية inner module، فنحن بحاجة لإضافة الشيفرة البرمجية التي نريد إجراء الاختبار عليها في الوحدة الخارجية outer module إلى نطاق scope الوحدة الداخلية، ونستخدم هنا <code>glob</code> بحيث يكون كل شيء نعرفه في الوحدة الخارجية متاحًا للوحدة <code>tests</code>.
</p>

<p>
	سمّينا الاختبار بالاسم <code>larger_can_hold_smaller</code> وأنشأنا نسختين من الهيكل <code>Rectangle</code> ومن ثم استدعينا الماكرو <code>assert!‎</code> ومرّرنا النتيجة باستدعاء <code>larger.can_hold(&amp;smaller)‎</code>. يجب أن يُعيد هذا التعبير القيمة <code>true</code> إذا اجتاز الاختبار بنجاح، دعنا نرى بأنفسنا.
</p>

<pre class="ipsCode">$ cargo test
   Compiling rectangle v0.1.0 (file:///projects/rectangle)
    Finished test [unoptimized + debuginfo] target(s) in 0.66s
     Running unittests src/lib.rs (target/debug/deps/rectangle-6584c4561e48942e)

running 1 test
test tests::larger_can_hold_smaller ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests rectangle

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
</pre>

<p>
	اجتاز الاختبار فعلًا. دعنا نضيف اختبارًا آخر بالتأكد من أن المستطيل الصغير لا يتسع داخل المستطيل الكبير:
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8722_20" style=""><span class="com">#[derive(Debug)]</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    width</span><span class="pun">:</span><span class="pln"> u32</span><span class="pun">,</span><span class="pln">
    height</span><span class="pun">:</span><span class="pln"> u32</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

impl </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn can_hold</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> other</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="typ">Rectangle</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="kwd">bool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">width </span><span class="pun">&gt;</span><span class="pln"> other</span><span class="pun">.</span><span class="pln">width </span><span class="pun">&amp;&amp;</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">height </span><span class="pun">&gt;</span><span class="pln"> other</span><span class="pun">.</span><span class="pln">height
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">#[cfg(test)]</span><span class="pln">
mod tests </span><span class="pun">{</span><span class="pln">
    use super</span><span class="pun">::*;</span><span class="pln">

    </span><span class="com">#[test]</span><span class="pln">
    fn larger_can_hold_smaller</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// --snip--</span><span class="pln">
        let larger </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">8</span><span class="pun">,</span><span class="pln">
            height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">};</span><span class="pln">
        let smaller </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln">
            height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">};</span><span class="pln">

        assert</span><span class="pun">!(</span><span class="pln">larger</span><span class="pun">.</span><span class="pln">can_hold</span><span class="pun">(&amp;</span><span class="pln">smaller</span><span class="pun">));</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="com">#[test]</span><span class="pln">
    fn smaller_cannot_hold_larger</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let larger </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">8</span><span class="pun">,</span><span class="pln">
            height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">};</span><span class="pln">
        let smaller </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln">
            height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">};</span><span class="pln">

        assert</span><span class="pun">!(!</span><span class="pln">smaller</span><span class="pun">.</span><span class="pln">can_hold</span><span class="pun">(&amp;</span><span class="pln">larger</span><span class="pun">));</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لأن النتيجة الصحيحة من الدالة <code>can_hold</code> هي <code>false</code> في هذه الحالة، فنحن بحاجة لنفي النتيجة قبل أن نمررها إلى الماكرو <code>assert!‎</code> وبالتالي سيجتاز الاختبار إذا أعادت <code>can_hold</code> القيمة <code>false</code>:
</p>

<pre class="ipsCode">$ cargo test
   Compiling rectangle v0.1.0 (file:///projects/rectangle)
    Finished test [unoptimized + debuginfo] target(s) in 0.66s
     Running unittests src/lib.rs (target/debug/deps/rectangle-6584c4561e48942e)

running 2 tests
test tests::larger_can_hold_smaller ... ok
test tests::smaller_cannot_hold_larger ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests rectangle

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
</pre>

<p>
	اجتزنا اختبارين متتالين، مرحى لنا. دعنا نرى ما الذي سيحدث الآن لنتائج الاختبار إذا أضفنا خطأً برمجيًا عن عمد في شيفرتنا البرمجية؛ نفعل ذلك بالتغيير من كتابة متن التابع <code>can_hold</code> باستبدال إشارة أكبر من إلى إشارة أصغر من عند المقارنة بين عرض كل من المستطيلين:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8722_22" style=""><span class="pln">impl </span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn can_hold</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> other</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="typ">Rectangle</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="kwd">bool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">width </span><span class="pun">&lt;</span><span class="pln"> other</span><span class="pun">.</span><span class="pln">width </span><span class="pun">&amp;&amp;</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">height </span><span class="pun">&gt;</span><span class="pln"> other</span><span class="pun">.</span><span class="pln">height
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نحصل على التالي عند تنفيذ الاختبارات:
</p>

<pre class="ipsCode">$ cargo test
   Compiling rectangle v0.1.0 (file:///projects/rectangle)
    Finished test [unoptimized + debuginfo] target(s) in 0.66s
     Running unittests src/lib.rs (target/debug/deps/rectangle-6584c4561e48942e)

running 2 tests
test tests::larger_can_hold_smaller ... FAILED
test tests::smaller_cannot_hold_larger ... ok

failures:

---- tests::larger_can_hold_smaller stdout ----
thread 'main' panicked at 'assertion failed: larger.can_hold(&amp;smaller)', src/lib.rs:28:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::larger_can_hold_smaller

test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass '--lib'
</pre>

<p>
	تنبّهت اختباراتنا للخطأ، لأن <code>larger.width</code> هو 8 و <code>smaller.width</code> هو 5، والمقارنة بين العرضَين في <code>can_hold</code> تُعيد النتيجة <code>false</code> إذ أن 8 ليست أقل من 5.
</p>

<h2>
	اختبار المساواة باستخدام الماكرو assert_eq!‎ و assert_ne!‎
</h2>

<p>
	نستخدم المساواة كثيرًا لاختبار البرنامج وذلك بين القيمة التي تعيدها الشيفرة البرمجية عند التنفيذ والقيمة التي تتوقع من الشيفرة البرمجية أن تعيدها، ويمكننا تحقيق ذلك باستخدام الماكرو <code>assert!‎</code> وتمرير تعبير باستخدام العامل <code>==</code>، إلا أن هناك طريقة أخرى أكثر شيوعًا موجودة في المكتبة القياسية ألا وهي الماكرو <code>assert_eq!‎</code> و <code>assert_ne!‎</code>؛ إذ يقارن كلًا من الماكرو المذكورَين سابقًا القيمة للتحقق من المساواة أو عدم المساواة، كما أننا سنحصل على طباعة للقيمتين إذا فشل الاختبار، بينما يدلنا الماكرو <code>assert!‎</code> فقط على حصوله على القيمة <code>false</code> من التعبير <code>==</code> دون طباعة القيم التي أدت لحصولنا للقيمة <code>false</code> في المقام الأول.
</p>

<p>
	نكتب في الشيفرة 11 دالة تدعى <code>add_two</code> تُضيف القيمة "2" إلى معاملها، ثم نفحص هذه الدالة باستخدام الماكرو <code>assert_eq!‎</code>.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8722_24" style=""><span class="pln">pub fn add_two</span><span class="pun">(</span><span class="pln">a</span><span class="pun">:</span><span class="pln"> i32</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> i32 </span><span class="pun">{</span><span class="pln">
    a </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">#[cfg(test)]</span><span class="pln">
mod tests </span><span class="pun">{</span><span class="pln">
    use super</span><span class="pun">::*;</span><span class="pln">

    </span><span class="com">#[test]</span><span class="pln">
    fn it_adds_two</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        assert_eq</span><span class="pun">!(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> add_two</span><span class="pun">(</span><span class="lit">2</span><span class="pun">));</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 7: اختبار الدالة <code>add_two</code> باستخدام الماكرو <code>assert_eq!‎</code>
</p>

<p>
	لنتحقّق من عمل الدالة:
</p>

<pre class="ipsCode">$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.58s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 1 test
test tests::it_adds_two ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
</pre>

<p>
	نمرّر القيمة "4" على أنها وسيط إلى الماكرو <code>assert_eq!‎</code> وهي قيمة تساوي قيمة استدعاء <code>add_two(2)‎</code>. السطر الخاص بالاختبار هو كالآتي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8910_7" style=""><span class="pln">test tests</span><span class="pun">::</span><span class="pln">it_adds_two </span><span class="pun">...</span><span class="pln"> ok </span></pre>

<p>
	وتُشير <code>ok</code> إلى أن نجاح الاختبار.
</p>

<p>
	دعنا نضيف خطأً برمجيًا متعمّدًا على شيفرتنا البرمجية لنرى كيف ستكون رسالة فشل الاختبار باستخدام الماكرو <code>assert_eq!‎</code>. لنغيّر من تطبيق <code>add_two</code> لنضيف قيمة "3" بدلًا من "2":
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8722_26" style=""><span class="pln">pub fn add_two</span><span class="pun">(</span><span class="pln">a</span><span class="pun">:</span><span class="pln"> i32</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> i32 </span><span class="pun">{</span><span class="pln">
    a </span><span class="pun">+</span><span class="pln"> </span><span class="lit">3</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نشغِّل الاختبارات مجددًا:
</p>

<pre class="ipsCode">$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.61s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 1 test
test tests::it_adds_two ... FAILED

failures:

---- tests::it_adds_two stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `4`,
 right: `5`', src/lib.rs:11:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::it_adds_two

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass '--lib'
</pre>

<p>
	نجح الاختبار بالتعرف على الخطأ، إذ أن الاختبار <code>it_adds_two</code> فشل وتخبرنا رسالة الخطأ أن سبب الفشل هو:
</p>

<pre class="ipsCode" id="ips_uid_7710_7">assertion failed: (left == right)</pre>

<p>
	بالإضافة لتحديد قيمة كل من <code>left</code> و <code>right</code>، وتساعدنا رسالة الخطأ هذه بالبدء بعملية تنقيح الأخطاء، إذ أن قيمة <code>left</code> هي "4" إلا أن <code>right</code> -القيمة التي حصلنا عليها من <code>add_two(2)‎</code>- هي "5". يصبح هذا الأمر مفيدًا جدًا عندما يكون لدينا الكثير من الاختبارات.
</p>

<p>
	يُطلق على معامل التأكد من المساواة للتوابع في بعض <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغات البرمجة</a> وأطر العمل اسم <code>expected</code> و <code>actual</code> ويكون ترتيب تحديد المعاملات مهمًّا، إلا أنهما يحملان اسم <code>left</code> و <code>right</code> في رست ولا يهمّ ترتيبهما في عند تمريرهما للماكرو، إذ يمكننا كتابة التأكيد assertion في الاختبار بالشكل:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8722_28" style=""><span class="pln">assert_eq</span><span class="pun">!(</span><span class="pln">add_two</span><span class="pun">(</span><span class="lit">2</span><span class="pun">),</span><span class="pln"> </span><span class="lit">4</span><span class="pun">)‎</span></pre>

<p>
	الذي سينتج رسالة الخطأ ذاتها التي حصلنا عليها سابقًا، وهي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8722_30" style=""><span class="pln">assertion failed</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">left </span><span class="pun">==</span><span class="pln"> right</span><span class="pun">)‎</span></pre>

<p>
	ينجح الاختبار باستخدام الماكرو <code>assert_ne!‎</code> إذا كانت القيمتين المُرّرتين له غير متساويتين، ويفشل في حال المساواة. نستفيد من هذا الماكرو عندما لا نعلم ماذا ستكون القيمة الناتجة تحديدًا إلا أننا نعلم أنها لا يجب أن تكون مساويةً لقيمة معينة؛ على سبيل المثال إذا كنا نختبر دالة يتغير دخلها بحسب يوم الأسبوع الذي ننفّذ فيه الاختبار، سيكون الأفضل في هذه الحالة هو التأكد من أن القيمة التي حصلنا عليها من خرج الدالة لا تساوي قيمة الدخل.
</p>

<p>
	يُستخدم كل من الماكرو <code>assert_eq!‎</code> و <code>assert_ne!‎</code> كلًا من العامل <code>==</code> و <code>=!</code> على الترتيب، وعندما تفشل عملية التأكيد، يطبع الماكرو معاملاته باستخدام تنسيق تنقيح الأخطاء، ما يعني أن القيم التي ستُقارن فيما بينها يجب أن تُطبّق السمتين <code>‏PartialEq</code> و <code>Debug</code>، وتطبق جميع الأنواع البدائية ومعظم أنواع المكتبة القياسية هاتين السمتين.
</p>

<p>
	ستحتاج لتطبيق <code>PartialEq</code> للهياكل والمعددات enums التي تعرّفها بنفسك للتأكد من مساواة النوعين، كما ستحتاج أيضًا لتطبيق <code>Debug</code> لطباعة القيم عندما يفشل التأكيد. كلا من السمتين المذكورتين هي سمات قابلة للاشتقاق derivable كما ذكرنا سابقًا في الشيفرة 12 من فصل <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%87%D9%8A%D8%A7%D9%83%D9%84-structs-%D9%84%D8%AA%D9%86%D8%B8%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1849/" rel="">استخدام الهياكل structs لتنظيم البيانات في لغة رست Rust</a>، وهذا يعني أننا نستطيع إضافة الشيفرة التالية مباشرةً في تعريف الهيكل أو المعدّد:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7710_9" style=""><span class="com">#[derive(PartialEq, Debug)]</span></pre>

<h2>
	إضافة رسائل فشل مخصصة
</h2>

<p>
	يمكنك إضافة رسائل مخصّصة لطباعتها مع رسالة الخطأ مثل معامل اختياري للماكرو <code>assert!‎</code> و <code>assert_eq!‎</code> و <code>assert_ne!‎</code>. تمرّر أي معاملات تُحدد بعد المعاملات المطلوبة للماكرو بدورها إلى الماكرو <code>format!‎</code> (الذي ناقشناه في مقال <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D9%84%D8%A7%D8%A6%D8%AD%D8%A9-%D9%85%D9%86-%D8%A7%D9%84%D9%82%D9%8A%D9%85-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%A3%D8%B4%D8%B9%D8%A9-vectors-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1875/" rel=""> تخزين لائحة من القيم باستخدام الأشعة Vectors</a>)، بحيث يمكنك تمرير سلسلة نصية منسّقة تحتوي على <code>{}</code> بمثابة موضع مؤقت والقيم بداخلها. الرسائل المخصصة مفيدة لتوثيق معنى التأكيد؛ إذ سيكون لديك فهم واضح عن المشكلة في الشيفرة البرمجية عندما يفشل الاختبار.
</p>

<p>
	على سبيل المثال، دعنا نفترض وجود دالة تحيّي الناس بالاسم ونريد أن نختبر إذا كان الاسم الذي نمرّره إلى الدالة يظهر في الخرج:
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8722_32" style=""><span class="pln">pub fn greeting</span><span class="pun">(</span><span class="pln">name</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    format</span><span class="pun">!(</span><span class="str">"Hello {}!"</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">#[cfg(test)]</span><span class="pln">
mod tests </span><span class="pun">{</span><span class="pln">
    use super</span><span class="pun">::*;</span><span class="pln">

    </span><span class="com">#[test]</span><span class="pln">
    fn greeting_contains_name</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let result </span><span class="pun">=</span><span class="pln"> greeting</span><span class="pun">(</span><span class="str">"Carol"</span><span class="pun">);</span><span class="pln">
        assert</span><span class="pun">!(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">contains</span><span class="pun">(</span><span class="str">"Carol"</span><span class="pun">));</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لم يُتّفق على متطلبات هذا البرنامج بعد، ونحن متأكدون أن النص "Hello" في بداية نص التحية سيتغير لاحقًا، لذا قررنا أننا لا نريد أن نعدل النص عند تغيير المتطلبات، وتحققنا بدلًا من ذلك من المساواة بين القيمة المُعادة من الدالة <code>greeting</code> للتأكد من أن الخرج يحتوي على النص الموجود في معامل الدخل.
</p>

<p>
	لنضيف خطأ برمجي متعمد على الشيفرة البرمجية بتغيير <code>greeting</code> بحيث لا تحتوي على الاسم لنرى ما ستكون نتيجة فشل الاختبار الافتراضي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8722_34" style=""><span class="pln">pub fn greeting</span><span class="pun">(</span><span class="pln">name</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"Hello!"</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تكون نتيجة تشغيل الاختبار على النحو التالي:
</p>

<pre class="ipsCode">$ cargo test
   Compiling greeter v0.1.0 (file:///projects/greeter)
    Finished test [unoptimized + debuginfo] target(s) in 0.91s
     Running unittests src/lib.rs (target/debug/deps/greeter-170b942eb5bf5e3a)

running 1 test
test tests::greeting_contains_name ... FAILED

failures:

---- tests::greeting_contains_name stdout ----
thread 'main' panicked at 'assertion failed: result.contains(\"Carol\")', src/lib.rs:12:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::greeting_contains_name

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass '--lib'
</pre>

<p>
	تشير النتيجة إلى أن التأكيد فشل في السطر الذي يحتويه، إلا أن رسالةً أكثر إفادة ستحتوي على القيمة الموجودة في دالة <code>greeting</code>. نضيف رسالة فشل مخصصة مؤلفة من سلسلة نصية منسّقة تحتوي على موضع مؤقت يُملأ بالقيمة الفعلية التي نحصل عليها من الدالة <code>greeting</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8722_36" style=""><span class="pln">    </span><span class="com">#[test]</span><span class="pln">
    fn greeting_contains_name</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let result </span><span class="pun">=</span><span class="pln"> greeting</span><span class="pun">(</span><span class="str">"Carol"</span><span class="pun">);</span><span class="pln">
        assert</span><span class="pun">!(</span><span class="pln">
            result</span><span class="pun">.</span><span class="pln">contains</span><span class="pun">(</span><span class="str">"Carol"</span><span class="pun">),</span><span class="pln">
            </span><span class="str">"Greeting did not contain name, value was `{}`"</span><span class="pun">,</span><span class="pln">
            result
        </span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	نحصل عندما نشغّل الاختبار الآن على رسالة خطأ أكثر وضوحًا:
</p>

<pre class="ipsCode">$ cargo test
   Compiling greeter v0.1.0 (file:///projects/greeter)
    Finished test [unoptimized + debuginfo] target(s) in 0.93s
     Running unittests src/lib.rs (target/debug/deps/greeter-170b942eb5bf5e3a)

running 1 test
test tests::greeting_contains_name ... FAILED

failures:

---- tests::greeting_contains_name stdout ----
thread 'main' panicked at 'Greeting did not contain name, value was `Hello!`', src/lib.rs:12:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::greeting_contains_name

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass '--lib'
</pre>

<p>
	يمكننا استخدام القيمة التي حصلنا عليها من خرج الاختبار مما سيساعدنا في تنقيح الخطأ الذي تسبب بذلك بدلًا من الفعل الذي توقعناه من الدالة.
</p>

<h2>
	التحقق من حالات الهلع باستخدام should_panic
</h2>

<p>
	من المهم بالإضافة للتحقق من القيم المعادة التحقق من أن شيفرتنا البرمجية تتعامل مع حالات الخطأ كما نتوقع، على سبيل المثال تذكر النوع <code>Guess</code> الذي أنشأناه في أمثلة سابقة في <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1920/" rel="">مقال الأخطاء والتعامل معها</a>، إذ استخدمت بعض الشيفرات البرمجية <code>Guess</code> واعتمدت على أن نسخ <code>Guess</code> ستحتوي على قيمة تترواح بين 1 و100. يمكننا كتابة اختبار يتسبب بحالة هلع إذا حاولنا إنشاء نسخة من <code>Guess</code> خارج المجال، وذلك عن طريق إضافة سمة <code>should_panic</code> إلى دالة الاختبار، وينجح الاختبار إذا كانت الشيفرة البرمجية داخل الدالة تهلع، وإلا يفشل الاختبار في حالة عدم هلع الدالة.
</p>

<p>
	توضح الشيفرة 8 اختبارًا يتأكد أن حالات الخطأ المتعلقة بالدالة <code>Guess::new</code> تحصل عندما نتوقع حصولها.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8722_38" style=""><span class="pln">pub </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Guess</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    value</span><span class="pun">:</span><span class="pln"> i32</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

impl </span><span class="typ">Guess</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pub fn </span><span class="kwd">new</span><span class="pun">(</span><span class="pln">value</span><span class="pun">:</span><span class="pln"> i32</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Guess</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> value </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> value </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">100</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            panic</span><span class="pun">!(</span><span class="str">"Guess value must be between 1 and 100, got {}."</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="typ">Guess</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> value </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">#[cfg(test)]</span><span class="pln">
mod tests </span><span class="pun">{</span><span class="pln">
    use super</span><span class="pun">::*;</span><span class="pln">

    </span><span class="com">#[test]</span><span class="pln">
    </span><span class="com">#[should_panic]</span><span class="pln">
    fn greater_than_100</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Guess</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="lit">200</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 8: اختبار أن حالة ما ستتسبب بالهلع <code>panic!‎</code>
</p>

<p>
	نضع السمة <code>‎#[should_panic]‎</code> بعد السمة <code>‎#[test]‎</code> وقبل دالة الاختبار التي نريد تطبيق السمة عليها.دعنا نلقي نظرةًعلى النتيجة بعد نجاح الاختبار:
</p>

<pre class="ipsCode">$ cargo test
   Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
    Finished test [unoptimized + debuginfo] target(s) in 0.58s
     Running unittests src/lib.rs (target/debug/deps/guessing_game-57d70c3acb738f4d)

running 1 test
test tests::greater_than_100 - should panic ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests guessing_game

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
</pre>

<p>
	يبدو أن الأمر على ما يرام. لنضيف خطأ برمجي عن عمد إلى شيفرتنا البرمجية بإزالة الشرط الذي يتسبب بهلع الدالة <code>new</code> إذا كانت القيمة أكبر من 100:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8722_41" style=""><span class="pln">impl </span><span class="typ">Guess</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pub fn </span><span class="kwd">new</span><span class="pun">(</span><span class="pln">value</span><span class="pun">:</span><span class="pln"> i32</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Guess</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> value </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            panic</span><span class="pun">!(</span><span class="str">"Guess value must be between 1 and 100, got {}."</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="typ">Guess</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> value </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يفشل الاختبار في الشيفرة 8 عندما نشغّله:
</p>

<pre class="ipsCode">$ cargo test
   Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
    Finished test [unoptimized + debuginfo] target(s) in 0.62s
     Running unittests src/lib.rs (target/debug/deps/guessing_game-57d70c3acb738f4d)

running 1 test
test tests::greater_than_100 - should panic ... FAILED

failures:

---- tests::greater_than_100 stdout ----
note: test did not panic as expected

failures:
    tests::greater_than_100

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass '--lib'
</pre>

<p>
	لا نحصل على رسالة مفيدة جدًافي هذه الحالة، لكن إذا نظرنا إلى دالة الاختبار نرى توصيفها بالتالي: <code>‎#[should_panic]‎</code>. حالة الفشل التي حصلنا عليها تعني أن الشيفرة البرمجية في دالة الاختبار لم تتسبب بحالة هلع.
</p>

<p>
	يمكن أن تصبح الاختبارات التي تستخدم <code>should_panic</code> غير دقيقة، إذ ينجح اختبار <code>should_pass</code> في حال هلع الاختبار لسبب مختلف عمّا كنا متوقعين، ولجعل اختبارات <code>should_panic</code> أكثر دقة، يمكننا إضافة معامل اختياري يدعى <code>expected</code> للسمة <code>should_panic</code>،وسيتأكد عندها الاختبار من أن رسالة الخطأ تحتوي على النص الذي زوّدناه به. على سبيل المثال، لنفترض أن الشيفرة البرمجية الخاصة بالدالة <code>Guess</code> في الشيفرة 9 احتوت على هلع الدالة <code>new</code> برسائل مختلفة بحسب إذا ما كانت القيمة صغيرة أو كبيرة.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8722_43" style=""><span class="pln">impl </span><span class="typ">Guess</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pub fn </span><span class="kwd">new</span><span class="pun">(</span><span class="pln">value</span><span class="pun">:</span><span class="pln"> i32</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Guess</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> value </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            panic</span><span class="pun">!(</span><span class="pln">
                </span><span class="str">"Guess value must be greater than or equal to 1, got {}."</span><span class="pun">,</span><span class="pln">
                value
            </span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> value </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">100</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            panic</span><span class="pun">!(</span><span class="pln">
                </span><span class="str">"Guess value must be less than or equal to 100, got {}."</span><span class="pun">,</span><span class="pln">
                value
            </span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="typ">Guess</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> value </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">#[cfg(test)]</span><span class="pln">
mod tests </span><span class="pun">{</span><span class="pln">
    use super</span><span class="pun">::*;</span><span class="pln">

    </span><span class="com">#[test]</span><span class="pln">
    </span><span class="com">#[should_panic(expected = "less than or equal to 100")]</span><span class="pln">
    fn greater_than_100</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Guess</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="lit">200</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 9: اختبار <code>panic!‎</code> برسالة هلع تحتوي على سلسلة نصية جزئية محددة
</p>

<p>
	سينجح الاختبار لأن القيمة التي نضعها في معامل <code>expected</code> الخاص بالسمة <code>should_panic</code> هي سلسلة نصية جزئية للرسالة التي تعرضها الدالة <code>Guess::new</code> عند الهلع. يمكننا تحديد كامل رسالة الهلع التي نتوقعها، وستكون في هذه الحالة:
</p>

<pre class="ipsCode">Guess value must be less than or equal to 100, got 200.‎
</pre>

<p>
	يعتمد ما تختار أن تحدده على رسالة الهلع إذا ما كانت مميزة أو ديناميكية ودقة الاختبار التي تريدها، وتُعد سلسلةً نصيةً جزئيةً لرسالة الهلع كافية في هذه الحالة للتأكد من أن الشيفرة البرمجية في دالة الاختبار تنفّذ حالة <code>else if value &gt; 100</code>.
</p>

<p>
	دعنا نضيف خطأ برمجي جديد مجددًا لرؤية ما الذي سيحصل للاختبار <code>should_panic</code> باستخدام رسالة <code>expected</code> وذلك بتبديل محتوى الكتلة <code>if value &lt; 1</code> مع <code>else if value &gt; 100</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8722_45" style=""><span class="pln">        </span><span class="kwd">if</span><span class="pln"> value </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            panic</span><span class="pun">!(</span><span class="pln">
                </span><span class="str">"Guess value must be less than or equal to 100, got {}."</span><span class="pun">,</span><span class="pln">
                value
            </span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> value </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">100</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            panic</span><span class="pun">!(</span><span class="pln">
                </span><span class="str">"Guess value must be greater than or equal to 1, got {}."</span><span class="pun">,</span><span class="pln">
                value
            </span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span></pre>

<p>
	يفشل الاختبار <code>should_panic</code> هذه المرة عند تشغيله:
</p>

<pre class="ipsCode">$ cargo test
   Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
    Finished test [unoptimized + debuginfo] target(s) in 0.66s
     Running unittests src/lib.rs (target/debug/deps/guessing_game-57d70c3acb738f4d)

running 1 test
test tests::greater_than_100 - should panic ... FAILED

failures:

---- tests::greater_than_100 stdout ----
thread 'main' panicked at 'Guess value must be greater than or equal to 1, got 200.', src/lib.rs:13:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
note: panic did not contain expected string
      panic message: `"Guess value must be greater than or equal to 1, got 200."`,
 expected substring: `"less than or equal to 100"`

failures:
    tests::greater_than_100

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass '--lib'
</pre>

<p>
	تشير رسالة الخطأ إلى أن هذا الاختبار هلع فعلًا كما توقعنا إلا أن رسالة الهلع لم تتوافق مع السلسلة النصية الجزئية:
</p>

<pre class="ipsCode">'Guess value must be less than or equal to 100'
</pre>

<p>
	إذ حصلنا على رسالة الهلع التالية في هذه الحالة:
</p>

<pre class="ipsCode">Guess value must be greater than or equal to 1, got 200.‎
</pre>

<p>
	لنبدأ الآن بالبحث عن الخطأ.
</p>

<h2>
	استخدام Result<t e="">‎ في الاختبارات</t>
</h2>

<p>
	تهلع جميع اختباراتنا لحد هذه اللحظة عند الفشل، إلا أنه يمكننا كتابة اختبارات تستخدم <code>Result&lt;T, E&gt;‎</code>. إليك اختبارًا من الشيفرة 1 إذ أعدنا كتابته باستخدام <code>Result&lt;T, E&gt;‎</code> ليعيد <code>Err</code> بدلًا من الهلع:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8722_48" style=""><span class="com">#[cfg(test)]</span><span class="pln">
mod tests </span><span class="pun">{</span><span class="pln">
    </span><span class="com">#[test]</span><span class="pln">
    fn it_works</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Result</span><span class="pun">&lt;(),</span><span class="pln"> </span><span class="typ">String</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">4</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">Ok</span><span class="pun">(())</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">Err</span><span class="pun">(</span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"two plus two does not equal four"</span><span class="pun">))</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	للدالة <code>it_works</code> الآن النوع المُعاد <code>Result&lt;(), String&gt;‎</code>، ونُعيد <code>Ok(())‎</code> عندما ينجح الاختبار و <code>Err</code> مع <code>Sring</code> بداخله عندما يفشل وذلك بدلًا من استدعاء الماكرو <code>assert_eq!‎</code>.
</p>

<p>
	تمكّنك كتابة الاختبارات بحيث تعيد <code>Result&lt;T, E&gt;‎</code> من استخدام عامل إشارة الاستفهام في محتوى الاختبار بحيث تكون وسيلةً ملائمةً لكتابة الاختبارات التي يجب أن تفشل إذا أعادت أي عملية داخلها المتغاير <code>Err</code>.
</p>

<p>
	لا يمكنك استخدام التوصيف <code>‎#[should_panic]‎</code> على الاختبارات التي تستخدم <code>Result&lt;T, E&gt;‎</code>. لا تستخدم عامل إشارة الاستفهام على <code>Result&lt;T, E&gt;‎</code> للتأكد من أن عملية ما تُعيد المتغاير <code>Err</code> بل استخدم <code>assert!(value.is_err())‎</code> بدلًا من ذلك.
</p>

<p>
	الآن وبعد أن تعلمنا الطرق المختلفة لكتابة الاختبارات، حان الوقت لننظر إلى ما يحدث عندما ننفذ الاختبارات ونكتشف الخيارات المختلفة التي يمكننا استخدامها مع <code>cargo test</code> في المقال التالي.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch11-00-testing.html" rel="external nofollow">Writing Automated Tests</a> من كتاب <a href="https://doc.rust-lang.org/stable/book/title-page.html" rel="external nofollow">The Rust Programming Language</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A8%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1960/" rel="">التحكم بتنفيذ الاختبارات في لغة رست Rust</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%AD%D9%82%D9%82-%D9%85%D9%86-%D8%A7%D9%84%D9%85%D8%B1%D8%A7%D8%AC%D8%B9-references-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AF%D9%88%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%AD%D9%8A%D8%A7%D8%A9-lifetimes-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1952/" rel="">التحقق من المراجع References باستخدام دورات الحياة Lifetimes في لغة رست</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-%D8%A7%D9%84%D8%A8%D8%AF%D8%A7%D9%8A%D8%A7%D8%AA-r1764/" rel="">تعلم لغة رست Rust: البدايات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%A7%D8%B3%D8%AA%D8%B1%D8%A7%D8%AA%D9%8A%D8%AC%D9%8A%D8%A7%D8%AA-%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%84%D9%84%D8%AA%D9%88%D8%A7%D9%81%D9%82-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-r1954/" rel="">استراتيجيات اختبارات مشاريع الويب للتوافق مع المتصفحات</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1959</guid><pubDate>Wed, 19 Apr 2023 16:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x62D;&#x642;&#x642; &#x645;&#x646; &#x627;&#x644;&#x645;&#x631;&#x627;&#x62C;&#x639; References &#x639;&#x628;&#x631; &#x62F;&#x648;&#x631;&#x627;&#x62A; &#x627;&#x644;&#x62D;&#x64A;&#x627;&#x629; Lifetimes &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A;</title><link>https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%AD%D9%82%D9%82-%D9%85%D9%86-%D8%A7%D9%84%D9%85%D8%B1%D8%A7%D8%AC%D8%B9-references-%D8%B9%D8%A8%D8%B1-%D8%AF%D9%88%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%AD%D9%8A%D8%A7%D8%A9-lifetimes-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1952/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_04/---References----Lifetimes----Rust.jpg.c1e2913025b44d331195733afef3d989.jpg" /></p>
<p>
	تعدّ دورات الحياة نوعًا آخر من الأنواع المعمّمة generic وقد استعملناها سابقًا دون معرفتنا، إذ تتأكد دورات الحياة أن المراجع references صالحة طوال حاجتنا لها بدلًا من التأكد أن لنوع ما سلوك معيّن.
</p>

<p>
	أغفلنا عند مناقشتنا للمراجع والاستعارة borrowing <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D9%84%D9%83%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1786/" rel="">سابقًا</a> أن كل مرجع له دورة حياة في رست، وهو نطاق المرجع الذي يبقى فيه صالحًا، وفي معظم الأحيان تكون دورات الحياة ضمنية واستنتاجية كما هو الحال بكون الأنواع استنتاجية، إذ أننا نحدد الأنواع فقط عندما يمكن وجود أكثر من نوع واحد في حالة ما، وبطريقة مشابهة، علينا أن نشير إلى دورات الحياة عندما ترتبط دورة حياة خاصة بمرجع بطرق عدّة مختلفة، إذ تتطلب منا رست تحديد العلاقة بين دورة حياة المعامل المعمّم للتأكد من أن المرجع الفعلي المستخدم وقت التشغيل سيكون صالحًا.
</p>

<p>
	مفهوم الإشارة إلى دورات الحياة غير موجود في معظم <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغات البرمجة</a>، لذا قد تشعر بأن محتوى هذا المقال غير مألوف بالنسبة لك، على الرغم من أننا لن نتكلم عن دورات الحياة بالتفصيل هنا بل سنتكلم عن الطرق الشائعة التي قد تصادف بها طريقة كتابة دورة حياة بحيث تألف هذا المفهوم.
</p>

<h2>
	منع المراجع المعلقة dangling references بدورات الحياة
</h2>

<p>
	هدف دورات الحياة الأساسي هو منع المراجع المعلّقة dangling references إذ تسبب للبرنامج إشارته إلى مرجع بيانات لا يتطابق مع البيانات التي نريدها، ألقِ نظرةً على البرنامج في الشيفرة 16، إذ يحتوي على نطاق خارجي وداخلي.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8971_8" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let r</span><span class="pun">;</span><span class="pln">

    </span><span class="pun">{</span><span class="pln">
        let x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span><span class="pln">
        r </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">x</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    println</span><span class="pun">!(</span><span class="str">"r: {}"</span><span class="pun">,</span><span class="pln"> r</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 16: محاولة لاستخدام مرجع خرجت قيمته عن النطاق
</p>

<p>
	<strong>ملاحظة</strong>: تصرّح الأمثلة في الشيفرة 16 والشيفرة 17 والشيفرة 23 عن <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%AF%D9%8A%D9%84-%D8%B9%D9%84%D9%8A%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1779/" rel="">متغيرات</a> دون إعطائها قيم أولية، لذا لا يوجد اسم المتغير في النطاق الخارجي. قد يبدو ذلك للوهلة الأولى تعارضًا مع مبدأ عدم وجود قيم فارغة null values في رست، إلا أننا سنحصل على <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1920/" rel="">خطأ</a> عند التصريف إذا حاولنا استخدام متغير قبل منحه قيمة، وهو ما يؤكد عدم سماح رست بوجود قيم فارغة.
</p>

<p>
	يصرح النطاق الخارجي عن متغير يدعى <code>r</code> دون إسناد قيمة أولية له، بينما يصرح النطاق الداخلي على متغير يدعى <code>x</code> بقيمة أولية 5. نحاول في النطاق الداخلي ضبط قيمة <code>r</code> لتصبح مرجعًا إلى القيمة <code>x</code> وعندما ينتهي النطاق الداخلي نحاول طباعة القيمة الموجودة في <code>r</code>. لن تُصرَّف هذه الشيفرة البرمجية وذلك لأن <code>r</code> يمثل مرجعًا لمتغير خرج عن النطاق قبل أن نستخدمه. إليك رسالة الخطأ:
</p>

<pre class="ipsCode">$ cargo run
   Compiling chapter10 v0.1.0 (file:///projects/chapter10)
error[E0597]: `x` does not live long enough
 --&gt; src/main.rs:6:13
  |
6 |         r = &amp;x;
  |             ^^ borrowed value does not live long enough
7 |     }
  |     - `x` dropped here while still borrowed
8 | 
9 |     println!("r: {}", r);
  |                       - borrow later used here

For more information about this error, try `rustc --explain E0597`.
error: could not compile `chapter10` due to previous error
</pre>

<p>
	لا "يعيش" المتغير <code>x</code> طويلًا، والسبب في ذلك هو أن <code>x</code> سيخرج عن النطاق عند انتهاء النطاق الداخلي في السطر 7، بينما سيبقى <code>r</code> صالحًا في النطاق الخارجي لأن نطاقه أكبر، وعندها نقول أنه "سيعيش" أطول. إذا سمحت رست لهذه الشيفرة البرمجية بالعمل فهذا يعني أن <code>r</code> سيمثل مرجعًا لمكان محرر في الذاكرة deallocated بعد خروج <code>x</code> من النطاق ولن يعمل أي شيء باستخدام <code>r</code> على النحو المطلوب، إذًا كيف تتحقق رست من صلاحية هذه الشيفرة البرمجية؟ باستخدام مدقق الاستعارة borrow checker.
</p>

<h2>
	مدقق الاستعارة
</h2>

<p>
	لمصرّف رست مدقق استعارة يقارن بين النطاقات لتحديد أن جميع عمليات الاستعارة صالحة، وتوضح الشيفرة 17 إصدارًا مماثلًا للشيفرة 16 ولكن بتوضيح دورة الحياة لكل من المتغيرات.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8971_11" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let r</span><span class="pun">;</span><span class="pln">                </span><span class="com">// ---------+-- 'a</span><span class="pln">
                          </span><span class="com">//          |</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">                     </span><span class="com">//          |</span><span class="pln">
        let x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span><span class="pln">        </span><span class="com">// -+-- 'b  |</span><span class="pln">
        r </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">x</span><span class="pun">;</span><span class="pln">           </span><span class="com">//  |       |</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">                     </span><span class="com">// -+       |</span><span class="pln">
                          </span><span class="com">//          |</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"r: {}"</span><span class="pun">,</span><span class="pln"> r</span><span class="pun">);</span><span class="pln"> </span><span class="com">//          |</span><span class="pln">
</span><span class="pun">}</span><span class="pln">                         </span><span class="com">// ---------+</span></pre>

<p style="text-align: center;">
	الشيفرة 17: دورة حياة <code>r</code>يشار إليها باستخدام <code>‎'a</code> بينما يشار إلى دورة حياة <code>x</code> باستخدام <code>‎'b</code>
</p>

<p>
	أشرنا هنا إلى دورة حياة <code>r</code> باستخدام <code>‎'a</code> ودورة حياة <code>x</code> باستخدام <code>‎'b</code>، وكما ترى، فإن الكتلة <code>‎'b</code> الداخلية أصغر بكثير من كتلة دورة حياة <code>‎'a</code> الخارجية. تقارن رست عند وقت التصريف ما بين حجم دورتي الحياة هاتين وتجد أن <code>r</code> لها دورة حياة <code>‎'a</code> إلا أنها تمثل مرجعًا إلى موقع ذاكرة دورة حياته <code>‎'b</code>، وبالتالي يُرفَض البرنامج لأن <code>‎'b</code> أقصر من <code>‎'a</code>، أي أن الغرض الذي نستخدم المرجع إليه يعيش أقصر من المرجع ذاته.
</p>

<p>
	نصلح الشيفرة البرمجية السابقة في الشيفرة 18، إذ لا يوجد لدينا مراجع معلقة بعد الآن، وتُصرَّف الشيفرة البرمجية بنجاح دون أي أخطاء.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8971_13" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span><span class="pln">            </span><span class="com">// ----------+-- 'b</span><span class="pln">
                          </span><span class="com">//           |</span><span class="pln">
    let r </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">x</span><span class="pun">;</span><span class="pln">           </span><span class="com">// --+-- 'a  |</span><span class="pln">
                          </span><span class="com">//   |       |</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"r: {}"</span><span class="pun">,</span><span class="pln"> r</span><span class="pun">);</span><span class="pln"> </span><span class="com">//   |       |</span><span class="pln">
                          </span><span class="com">// --+       |</span><span class="pln">
</span><span class="pun">}</span><span class="pln">                         </span><span class="com">// ----------+</span></pre>

<p style="text-align: center;">
	الشيفرة 18: مرجع صالح لأن للبيانات دورة حياة أطول من المرجع
</p>

<p>
	دورة حياة <code>x</code> التي تدعى <code>‎'b</code> أكبر من <code>‎'a</code>، مما يعني أن <code>r</code> يمكن أن يمثل مرجعًا للمتغير <code>x</code>، لأن رست تعلم أن <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%B1%D8%A7%D8%AC%D8%B9-references-%D9%88%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D8%A7%D8%B1%D8%A9-borrowing-%D9%88%D8%A7%D9%84%D8%B4%D8%B1%D8%A7%D8%A6%D8%AD-slices-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1787/" rel="">المرجع</a> في <code>r</code> صالح ما دام <code>x</code> صالح.
</p>

<p>
	الآن وبعد أن تعرفت على دورات حياة المراجع وكيف تحلل رست دورات الحياة للتأكد من أن المراجع ستكون دائمًا صالحة، حان وقت التعرف إلى دورات الحياة المعمّمة الخاصة بالمعاملات والقيمة المُعادة في سياق <a href="https://academy.hsoub.com/programming/rust/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-functions-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%82%D8%A7%D8%AA-comments-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%A7%D8%B3%D8%AA-rust-r1781/" rel="">الدوال</a>.
</p>

<h2>
	دورات الحياة المعممة في الدوال
</h2>

<p>
	دعنا نكتب دالةً تُعيد أطول شريحة نصية string slice من شريحتين نصيتين، إذ ستأخذ هذه الدالة شريحتين نصيتين وتُعيد شريحةً نصيةً واحدة.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8971_15" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let string1 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"abcd"</span><span class="pun">);</span><span class="pln">
    let string2 </span><span class="pun">=</span><span class="pln"> </span><span class="str">"xyz"</span><span class="pun">;</span><span class="pln">

    let result </span><span class="pun">=</span><span class="pln"> longest</span><span class="pun">(</span><span class="pln">string1</span><span class="pun">.</span><span class="pln">as_str</span><span class="pun">(),</span><span class="pln"> string2</span><span class="pun">);</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"The longest string is {}"</span><span class="pun">,</span><span class="pln"> result</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 19: دالة <code>main</code> تستدعي الدالة <code>longest</code> لإيجاد أطول شريحة نصية من شريحتين نصيتين
</p>

<p>
	يجب أن تطبع الشيفرة 19 بعد تطبيق الدالة <code>longest</code> ما يلي:
</p>

<pre class="ipsCode">The longest string is abcd
</pre>

<p>
	لاحظ أننا نريد أن تأخذ الدالة شرائح النصية وهي مراجع وليست سلاسل نصية لأننا لا نريد للدالة <code>longest</code> أن <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D9%84%D9%83%D9%8A%D8%A9-ownership-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1786/" rel="">تأخذ ملكية معاملاتها</a>. عُد للمقال الذي تكلمنا فيه عن <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%A7%D9%84%D9%85%D8%B1%D8%A7%D8%AC%D8%B9-references-%D9%88%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D8%A7%D8%B1%D8%A9-borrowing-%D9%88%D8%A7%D9%84%D8%B4%D8%B1%D8%A7%D8%A6%D8%AD-slices-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1787/" rel="">شرائح السلاسل النصية مثل معاملات</a> لمعرفة المزيد حول سبب استخدامنا للمعاملات في الشيفرة 19 كما هي.
</p>

<p>
	لن تُصرّف الشيفرة البرمجية إذا حاولنا كتابة الدالة <code>longest</code> كما هو موضح في الشيفرة 20.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8971_17" style=""><span class="pln">fn longest</span><span class="pun">(</span><span class="pln">x</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">str</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">str </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> x</span><span class="pun">.</span><span class="pln">len</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> y</span><span class="pun">.</span><span class="pln">len</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        x
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        y
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 20: تنفيذ الدالة <code>longest</code> الذي يُعيد أطول شريحة نصية من شريحتين إلا أنه لا يُصرَّف بنجاح
</p>

<p>
	نحصل على الخطأ التالي الذي يتحدث عن دورات الحياة:
</p>

<pre class="ipsCode">$ cargo run
   Compiling chapter10 v0.1.0 (file:///projects/chapter10)
error[E0106]: missing lifetime specifier
 --&gt; src/main.rs:9:33
  |
9 | fn longest(x: &amp;str, y: &amp;str) -&gt; &amp;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 `x` or `y`
help: consider introducing a named lifetime parameter
  |
9 | fn longest&lt;'a&gt;(x: &amp;'a str, y: &amp;'a str) -&gt; &amp;'a str {
  |           ++++     ++          ++          ++

For more information about this error, try `rustc --explain E0106`.
error: could not compile `chapter10` due to previous error
</pre>

<p>
	تساعدنا رسالة الخطأ في معرفة أن النوع المُعاد يجب أن يكون له معامل بدورة حياة معممة لأن رست لا تعلم إذا كان المرجع المُعاد يمثل مرجعًا إلى <code>x</code> أو <code>y</code>، وفي الحقيقة لا نعلم نحن أيضًا بدورنا لأن كتلة <code>if</code> في متن الدالة يُعيد مرجعًا للمتغير <code>x</code> وكتلة <code>else</code> تُعيد مرجعًا للمتغير <code>y</code>.
</p>

<p>
	لا نعلم القيم الثابتة التي ستُمرر لهذه الدالة عندما نعرفها، لذا لا نعلم إذا ما كانت حالة <code>if</code> محققة أو حالة <code>else</code>، كما أننا لا نعرف دورة الحياة الثابتة للمراجع التي ستُمرر للدالة، لذا لا يمكننا النظر إلى النطاق كما فعلنا في الشيفرة 17 والشيفرة 18 للتأكد إذا ما كان المرجع المُعاد صالحًا دومًا، ولا يمكن لمدقق الاستعارة معرفة ذلك أيضًا لأنه لا يعرف أيّ من دورتي الحياة لكل من <code>x</code> و <code>y</code> ستكون مرتبطة بدورة الحياة الخاصة بالقيمة المُعادة؛ ولتصحيح هذا الخطأ نُضيف معاملًا ذا دورة حياة معممة يعرّف العلاقة ما بين المراجع حتى يستطيع مدقق الاستعارة إجراء تحليله.
</p>

<h2>
	طريقة كتابة دورة الحياة
</h2>

<p>
	لا تغيّر طريقة كتابة <a href="https://academy.hsoub.com/marketing/core-concepts-of-marketing/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AF%D9%88%D8%B1%D8%A9-%D8%AD%D9%8A%D8%A7%D8%A9-%D8%A7%D9%84%D9%85%D9%86%D8%AA%D8%AC/" rel="">دورة الحياة</a> على طول حياة المراجع، إذ تصف طريقة الكتابة العلاقة ما بين دورات الحياة لعدة مراجع بين بعضها بعضًا دون التأثير على دورات الحياة بذاتها. يمكن أن تقبل الدوال المراجع بأي دورة حياة بتحديد معامل دورة حياة معممة كما تقبل أي نوع عند تخصيص معامل من نوع معمم في بصمتها.
</p>

<p>
	طريقة كتابة دورة الحياة غير مألوفة جدًا، إذ يجب أن تبدأ أسماء معاملات دورات الحياة بالفاصلة العليا <code>'</code> وعادةً ما تكون أسمائها قصيرة ومكتوبة بأحرف قصيرة كما هو الحال مع الأنواع المعممة. يستخدم معظم الناس الاسم <code>‎'a</code> بمثابة اسم أول دورة حياة، ومن ثم نضع معامل دورة الحياة بعد إشارة <code>&amp;</code> الخاصة بالمرجع باستخدام المسافة للفصل بين طريقة كتابة دورة الحياة ونوع المرجع.
</p>

<p>
	إليك بعض الأمثلة على ذلك: مرجع لقيمة من نوع <code>i32</code> دون معامل دورة حياة، ومرجع لقيمة من نوع <code>i32</code> بمعامل دورة حياة يدعى <code>‎'a</code> ومرجع قابل للتعديل mutable لقيمة من نوع <code>i32</code> بالاسم <code>'a</code> ذاته.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8971_19" style=""><span class="pun">&amp;</span><span class="pln">i32        </span><span class="com">// مرجع</span><span class="pln">
</span><span class="pun">&amp;</span><span class="str">'</span><span class="pln">a i32     </span><span class="com">// مرجع مع دورة حياة صريحة</span><span class="pln">
</span><span class="pun">&amp;</span><span class="str">'a mut i32 // مرجع قابل للتعديل مع دورة حياة صريحة</span></pre>

<p>
	لا تعني كتابة دورة الحياة بمفردها بالشكل السابق الكثير، إذ أن الهدف من هذه الطريقة هو إخبار رست بعلاقة المراجع فيما بينها في معاملات دورة الحياة المعممة. دعنا ننظر إلى كيفية تحقيق ذلك في سياق الدالة <code>longest</code>.
</p>

<h2>
	توصيف دورة الحياة في بصمات الدالة
</h2>

<p>
	نحتاج للتصريح عن معاملات دورة الحياة المعممة داخل أقواس مثلثة حتى نستطيع استخدام توصيف دورة الحياة في بصمات الدوال، وذلك بين اسم الدالة وقائمة معاملاتها كما فعلنا سابقًا في معاملات النوع المعمم.
</p>

<p>
	نريد من بصمة الدالة أن توضح القيود التالية: سيكون المرجع المُعاد صالح طالما أن كلا المعاملين صالحان؛ وهذه هي العلاقة بين دورات حياة المعاملات والقيمة المعادة. سنسمّي دورة حياة بالاسم <code>‎'a</code>، ثم نُضيفها لكل مرجع كما هو موضح في الشيفرة 21.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8971_21" style=""><span class="pln">fn longest</span><span class="pun">&lt;</span><span class="str">'a&gt;(x: &amp;'</span><span class="pln">a str</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="str">'a str) -&gt; &amp;'</span><span class="pln">a str </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> x</span><span class="pun">.</span><span class="pln">len</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> y</span><span class="pun">.</span><span class="pln">len</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        x
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        y
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 21: تعريف الدالة <code>longest</code> الذي يحدد أن دورة الحياة لجميع المراجع في بصمة الدالة هي <code>‎'a</code>
</p>

<p>
	يجب أن تعمل الشيفرة البرمجية السابقة بنجاح وأن تمنحنا النتيجة المرجوة عند استخدامها ضمن الدالة <code>main</code> كما فعلنا في الشيفرة 19 السابقة.
</p>

<p>
	تخبر بصمة الدالة رست بأن الدالة تأخذ معاملين لبعض دورات الحياة <code>‎'a</code> وكلاهما شريحة نصية يعيشان على الأقل بطول دورة حياة <code>‎'a</code>، كما تخبر بصمة الدالة رست بأن شريحة السلسلة النصية المُعادة من الدالة ستعيش على الأقل بطول دورة الحياة <code>‎'a</code>، وهذا يعني عمليًا أن دورة حياة المرجع المُعاد من الدالة <code>longest</code> مماثلة لأقصر دورة حياة من دورات حياة القيم التي استخدمنا مراجعها في وسطاء الدالة، وهذه هي العلاقة التي نريد أن تستخدمها رست عند تحليل هذه الشيفرة البرمجية.
</p>

<p>
	تذكر أننا لا نعدّل من دورات حياة القيم الممررّة أو المُعادة عندما نحدد دورة الحياة المعاملات في بصمة الدالة، وإنما نحدد أنه يجب على مدقق الاستعارة أن يرفض أي قيمة لا تتوافق مع القيود المذكورة. لاحظ أن الدالة <code>longest</code> لا تحتاج لمعرفة أيّ من المتغيرين <code>x</code> و <code>y</code> سيعيش لمدة أطول، بل فقط بحاجة لمعرفة أن نطاق ما سيُستبدل بدورة الحياة <code>‎'a</code> التي ستطابق بصمة الدالة.
</p>

<p>
	نكتب توصيف دورات الحياة عند استخدامها مع الدوال في بصمة الدالة وليس في متنها، إذ يصبح توصيف دورة الحياة جزءًا من عقد contract الدالة كما هو الحال بالنسبة للأنواع ضمن بصمة الدالة. احتواء بصمة الدالة على عقد دورة الحياة يعني أن التحليل الذي يجريه مصرف رست سيصبح أبسط، وإذا وُجدت مشكلة بطريقة توصيف الدالة أو طريقة استدعائها يمكن لأخطاء المصرف أن تُشير إلى ذلك الجزء ضمن الشيفرة البرمجية والقيود التي خرقتها بصورةٍ أدقّ.
</p>

<p>
	إذا قدّم مصرف رست بعض الاستنتاجات حول العلاقة المقصودة لدورات الحياة، سيكون في هذه الحالة فادرًا على إعلامنا باستخدام الشيفرة الخاصة بنا وفق عدة خطوات لكنه سيكون بعيدًا عن السبب الحقيقي وراء المشكلة.
</p>

<p>
	عند تمرير المراجع الثابتة إلى <code>longest</code> تكون دورة الحياة الثابتة المُستبدلة بدورة الحياة <code>‎'a</code> جزءًا من نطاق <code>x</code> الذي يتداخل مع نطاق <code>y</code>، بمعنى آخر، تحصل دورة الحياة المعممة <code>‎'a</code> على دورة حياة ثابتة مساوية إلى أصغر دورة حياة (أصغر دورة بين الدورتين الخاصة بالمتغير <code>y</code> والمتغير <code>x</code>).
</p>

<p>
	دعنا ننظر إلى نتيجة استخدام توصيف دورة الحياة وكيف يقيّد ذلك من دالة <code>longest</code> بتمرير المراجع التي لها دورات حياة ثابتة مختلفة، وتمثّل الشيفرة 22 مثالًا مباشرًا على ذلك.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8971_23" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let string1 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"long string is long"</span><span class="pun">);</span><span class="pln">

    </span><span class="pun">{</span><span class="pln">
        let string2 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"xyz"</span><span class="pun">);</span><span class="pln">
        let result </span><span class="pun">=</span><span class="pln"> longest</span><span class="pun">(</span><span class="pln">string1</span><span class="pun">.</span><span class="pln">as_str</span><span class="pun">(),</span><span class="pln"> string2</span><span class="pun">.</span><span class="pln">as_str</span><span class="pun">());</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"The longest string is {}"</span><span class="pun">,</span><span class="pln"> result</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 22: استخدام الدالة <code>longest</code> مع مراجع لقيم من نوع <code>String</code> تمتلك دورات حياة ثابتة مختلفة
</p>

<p>
	تكون القيمة <code>string1</code> صالحةً في المثال السابق حتى الوصول لنهاية النطاق الخارجي، بينما تبقى <code>string2</code> صالحة حتى نهاية النطاق الداخلي، وأخيرًا تمثل <code>result</code> مرجعًا لقيمة صالحة حتى نهاية النطاق الخارجية. نفّذ الشيفرة البرمجية السابقة وسترى أن مدقق الاستعارة لن يعترض على الشيفرة البرمجية وستُصرَّف وتطبع ما يلي:
</p>

<pre class="ipsCode">The longest string is long string is long
</pre>

<p>
	دعنا نجرّب مثالًا يوضح أن دورة حياة المرجع في <code>result</code> يجب أن تكون أصغر من دورة حياة كلا الوسيطين؛ إذ سننقل التصريح عن المتغير <code>result</code> خارج النطاق الداخلي مع المحافظة على عملية إسناد قيمة إلى المتغير <code>result</code> داخل النطاق حيث توجد <code>string2</code>، ثم سننقل <code>println!‎</code> الذي يستخدم <code>result</code> خارج النطاق الداخلي بعد انتهائه. لن تُصرَّف الشيفرة 23 بنجاح.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8971_25" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let string1 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"long string is long"</span><span class="pun">);</span><span class="pln">
    let result</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        let string2 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"xyz"</span><span class="pun">);</span><span class="pln">
        result </span><span class="pun">=</span><span class="pln"> longest</span><span class="pun">(</span><span class="pln">string1</span><span class="pun">.</span><span class="pln">as_str</span><span class="pun">(),</span><span class="pln"> string2</span><span class="pun">.</span><span class="pln">as_str</span><span class="pun">());</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"The longest string is {}"</span><span class="pun">,</span><span class="pln"> result</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 23: محاولة استخدام <code>result</code> بعد خروج <code>string2</code> من النطاق
</p>

<p>
	نحصل على رسالة الخطأ التالية عندما نحاول تصريف الشيفرة البرمجية:
</p>

<pre class="ipsCode">$ cargo run
   Compiling chapter10 v0.1.0 (file:///projects/chapter10)
error[E0597]: `string2` does not live long enough
 --&gt; src/main.rs:6:44
  |
6 |         result = longest(string1.as_str(), string2.as_str());
  |                                            ^^^^^^^^^^^^^^^^ borrowed value does not live long enough
7 |     }
  |     - `string2` dropped here while still borrowed
8 |     println!("The longest string is {}", result);
  |                                          ------ borrow later used here

For more information about this error, try `rustc --explain E0597`.
error: could not compile `chapter10` due to previous error
</pre>

<p>
	يوضح الخطأ أنه يجب على <code>result</code> أن يكون صالحًا حتى تُنفَّذ التعليمة <code>println!‎</code>، كما يجب على المتغير <code>string2</code> أن يكون صالحًا حتى نهاية النطاق الخارجي، وتعلم رست ذلك بسبب توصيفنا لدورات حياة معاملات الدالة والقيم المُعادة باستخدام معامل دورة الحياة ذاته <code>‎'a</code>.
</p>

<p>
	يمكننا النظر إلى هذه الشيفرة البرمجية على أننا بشر ورؤية أن <code>string1</code> أطول من <code>string2</code> وبالتالي سيحتوي المتغير <code>result</code> على مرجع للمتغير <code>string1</code>، ولأن <code>string1</code> لم يخرج من النطاق بعد، فسيبقى مرجع <code>string1</code> صالحًا حتى تستخدمه تعليمة <code>!println</code>، إلا أن المصرف لا ينظر إلى المرجع بكونه صالحًا في هذه الحالة إذ أننا أخبرنا رست أن دورة حياة المرجع المُعاد بواسطة الدالة <code>longest</code> هو بطول أصغر دورة حياة مرجع مُمرّر لها، وبالتالي لا يسمح مدقق الاستعارة للشيفرة 23 بامتلاك الفرصة للحصول على مرجع غير صالح.
</p>

<p>
	جرّب كتابة المزيد من الأمثلة لتجربة الحالات والقيم ودورات حياة المراجع المختلفة المُمرّر إلى الدالة <code>longest</code> ولاحظ كيفية استخدام المرجع المُعاد، وتنبّأ فيما إذا كانت تجربتك ستُصرَّف ويوافق عليها مدقق الاستعارة أم لا قبل أن تحاول تصريفها، ومن ثم جرّب تصريفها لترى إن كنت مصيبًا أم لا.
</p>

<h2>
	التفكير في سياق دورات الحياة
</h2>

<p>
	تعتمد الطريقة التي تحدد فيها دورة حياة المعاملات على الغرض من الدالة، فعلى سبيل المثال إذا عدّلت من كتابة الدالة <code>longest</code> لتُعيد دائمًا المعامل الأول بدلًا من المعامل الذي يمثل أطول شريحة نصية فلن نحتاج عندئذ لتحديد دورة حياة المعامل <code>y</code>. تُصرَّف الشيفرة البرمجية التالية بنجاح:
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8971_27" style=""><span class="pln">fn longest</span><span class="pun">&lt;</span><span class="str">'a&gt;(x: &amp;'</span><span class="pln">a str</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="str">'</span><span class="pln">a str </span><span class="pun">{</span><span class="pln">
    x
</span><span class="pun">}</span></pre>

<p>
	حددنا معامل دورة حياة مُمثّل بالاسم <code>‎'a</code> للمعامل <code>x</code> والقيمة المُعادة، إلا أننا لم نحدد دورة حياة للمعامل <code>y</code> لأن ليس لدورة حياة <code>y</code> أي علاقة بدورة حياة <code>x</code> أو القيمة المُعادة.
</p>

<p>
	يجب أن يطابق معامل دورة حياة القيمة المُعادة دورة حياة أحد من المعاملات عند إعادة مرجع من دالة ما، وإذا لم يشير المرجع المُعاد إلى واحد من المعاملات فيجب أن يشير إلى قيمة أُنشئت داخل الدالة ذاتها، إلا أن هذا المرجع سيكون مرجعًا معلَّقًا، لأن القيمة ستخرج من النطاق في نهاية الدالة. ألقِ نظرةً على المحاولة التالية لتطبيق الدالة <code>longest</code> التي لن تُصرَّف بنجاح:
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8971_29" style=""><span class="pln">fn longest</span><span class="pun">&lt;</span><span class="str">'a&gt;(x: &amp;str, y: &amp;str) -&gt; &amp;'</span><span class="pln">a str </span><span class="pun">{</span><span class="pln">
    let result </span><span class="pun">=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"really long string"</span><span class="pun">);</span><span class="pln">
    result</span><span class="pun">.</span><span class="pln">as_str</span><span class="pun">()</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	على الرغم من أننا حددنا معامل دورة الحياة <code>‎'a</code> للنوع المُعاد إلا أن الشيفرة البرمجية لن تُصرَّف لأن دورة حياة القيمة المُعادة غير مرتبطة بدورة حياة المعاملات إطلاقًا. إليك رسالة الخطأ التي سنحصل عليها:
</p>

<pre class="ipsCode">$ cargo run
   Compiling chapter10 v0.1.0 (file:///projects/chapter10)
error[E0515]: cannot return reference to local variable `result`
  --&gt; src/main.rs:11:5
   |
11 |     result.as_str()
   |     ^^^^^^^^^^^^^^^ returns a reference to data owned by the current function

For more information about this error, try `rustc --explain E0515`.
error: could not compile `chapter10` due to previous error
</pre>

<p>
	تكمن المشكلة هنا في أن <code>result</code> يخرج من النطاق ويُحرَّر من الذاكرة بنهاية الدالة <code>longest</code>، إلا أننا نحاول أيضًا إعادة مرجع للقيمة <code>result</code> من الدالة في ذات الوقت، ولا يوجد هناك أي وسيلة لتحديد معاملات دورة الحياة بحيث نتخلص من المرجع المُعلَّق ولن تسمح لنا رست بإنشاء مرجع معلّق. الحل الأمثل في هذه الحال هو بجعل القيمة المُعادة <a href="https://academy.hsoub.com/programming/rust/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-data-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1780/" rel="">نوع بيانات</a> مملوك owned data type بدلًا من استخدام مرجع، بحيث تكون الدالة المُستدعاة حينها مسؤولة عن تحرير القيمة فيما بعد.
</p>

<p>
	يتمثّل توصيف دورة الحياة بربط دورات حياة معاملات مختلفة والقيم المُعادة من الدوال، بحيث تحصل رست على معلومات كافية بعد الربط للسماح بعمليات آمنة على الذاكرة ومنع عمليات قد تتسبب بالحصول على مؤشرات معلّقة أو تخرق أمان الذاكرة.
</p>

<h2>
	توصيف دورة الحياة في تعاريف الهيكل
</h2>

<p>
	كانت <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%87%D9%8A%D8%A7%D9%83%D9%84-structs-%D9%84%D8%AA%D9%86%D8%B8%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1849/" rel="">الهياكل</a> التي عرفناها لحد اللحظة تحتوي على أنواع مملوكة، إلا أنه يمكننا تعريف الهياكل بحيث تحتوي على مراجع وفي هذه الحالة علينا توصيف دورة حياة لكل من المراجع في تعريف الهيكل. تحتوي الشيفرة 24 على هيكل يدعى <code>ImportantExcerpt</code> يحتوي على شريحة سلسلة نصية.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8971_31" style=""><span class="kwd">struct</span><span class="pln"> </span><span class="typ">ImportantExcerpt</span><span class="pun">&lt;</span><span class="str">'</span><span class="pln">a</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    part</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="str">'</span><span class="pln">a str</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let novel </span><span class="pun">=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"Call me Ishmael. Some years ago..."</span><span class="pun">);</span><span class="pln">
    let first_sentence </span><span class="pun">=</span><span class="pln"> novel</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="str">'.'</span><span class="pun">).</span><span class="pln">next</span><span class="pun">().</span><span class="pln">expect</span><span class="pun">(</span><span class="str">"Could not find a '.'"</span><span class="pun">);</span><span class="pln">
    let i </span><span class="pun">=</span><span class="pln"> </span><span class="typ">ImportantExcerpt</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        part</span><span class="pun">:</span><span class="pln"> first_sentence</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 24: هيكل يحتوي على مرجع، وبذلك يتطلب توصيف دورة الحياة
</p>

<p>
	يحتوي الهيكل على حقل يدعى <code>part</code> يخزّن داخله شريحة سلسلة نصية وهي مرجع، وينبغي علينا هنا التصريح عن اسم معامل دورة الحياة المعممة داخل أقواس مثلثة بعد اسم الهيكل كما هو الحال مع الأنواع المعممة وذلك حتى يتسنّى لنا استخدام معامل دورة الحياة في متن تعريف الهيكل، وتعني طريقة الكتابة هذه أنه لا يوجد أي نسخة من <code>ImportantExcerpt</code> تعيش أطول من المرجع الموجود في الحقل <code>part</code>.
</p>

<p>
	تُنشئ الدالة <code>main</code> هنا نسخةً من الهيكل <code>ImportantExcerpt</code> بحيث يحتوي على مرجع للجملة الأولى من <code>String</code> والمملوك من قبل المتغير <code>novel</code>، والبيانات في <code>novel</code> موجودةٌ قبل إنشاء نسخة من <code>ImportantExcerpt</code>، بالإضافة إلى ذلك فإن <code>novel</code> لا تخرج من النطاق إلى أن يخرج <code>ImportantExcerpt</code> من النطاق. إذًا، فالمرجع الموجود في نسخة <code>ImportantExcerpt</code> صالح.
</p>

<h2>
	إخفاء دورة الحياة
</h2>

<p>
	تعلمنا أنه لكل مرجع ما دورة حياة ويجب أن نحدّد معاملات دورة الحياة للدوال أو للهياكل التي تستخدم المراجع، إلا أننا كتبنا دالةً في السابق (الشيفرة 9) كما هي موضحة في الشيفرة 25، وقد صُرَِفت بنجاح دون استخدام توصيف دورة الحياة.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8971_33" style=""><span class="pln">fn first_word</span><span class="pun">(</span><span class="pln">s</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">str </span><span class="pun">{</span><span class="pln">
    let bytes </span><span class="pun">=</span><span class="pln"> s</span><span class="pun">.</span><span class="pln">as_bytes</span><span class="pun">();</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">i</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">item</span><span class="pun">)</span><span class="pln"> in bytes</span><span class="pun">.</span><span class="pln">iter</span><span class="pun">().</span><span class="pln">enumerate</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> item </span><span class="pun">==</span><span class="pln"> b</span><span class="str">' '</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">s</span><span class="pun">[</span><span class="lit">0.</span><span class="pun">.</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="pun">&amp;</span><span class="pln">s</span><span class="pun">[..]</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 25: دالة عرفناها سابقًا وصرّفت بنجاح دون استخدام توصيف دورة الحياة على الرغم من كون كل من المعاملات والقيمة المعادة مراجع
</p>

<p>
	السبب في تصريف الشيفرة السابقة بنجاح هو سبب تاريخي، إذ لن تُصرَّف الشيفرة البرمجية هذه في الإصدارات السابقة من رست (قبل 1.0)، وذلك لحاجة كل مرجع لدورة حياة صريحة. إليك ما ستبدو عليه بصمة الدالة في ذلك الوقت من تطوير اللغة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8971_35" style=""><span class="pln">fn first_word</span><span class="pun">&lt;</span><span class="str">'a&gt;(s: &amp;'</span><span class="pln">a str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="str">'a str {</span></pre>

<p>
	وجد فريق تطوير رست بعد كتابة الكثير من الشيفرات البرمجية باستخدام اللغة أن معظم مبرمجي رست يُدخلون توصيف دورة الحياة ذاته مرةً بعد الأخرى في حالات معيّنة، وكان يمكن توقع هذه الحالات واتباعها بأنماط للتعرف عليها، وبالتالي برمج المطوّرون هذه الأنماط إلى شيفرة المصرّف البرمجية بحيث يتعرّف عليها مدقق الاستعارة ويستنتج دورات الحياة في هذه الحالات دون الحاجة لكتابة توصيف دورة الحياة مباشرةً.
</p>

<p>
	هذه النقطة في تاريخ تطوير رست مهمة لأنه من الممكن ظهور المزيد من الأنماط مستقبلًا وإضافتها إلى المصرف، وبذلك قد لا نحتاج لاستخدام توصيف دورات الحياة مباشرةً في العديد من الحالات.
</p>

<p>
	تُدعى هذه الأنماط الموجودة لتحليل المراجع بقواعد إخفاء دورة الحياة lifetime elision rules، إلا أن هذه القواعد ليست للمبرمجين حتى يتبعونها بل هي مجموعة من الحالات التي سينظر إليها المصرّف، إذ لن تحتاج لاستخدام توصيف دورات الحياة مباشرةً إذا كانت شيفرتك البرمجية تندرج ضمن واحدة من هذه الحالات.
</p>

<p>
	لا تقدّم قواعد الإخفاء القدرة على الاستنتاج بصورةٍ كاملة، إذ لن يستطيع المصرف تخمين دورات الحياة الخاصة بالمراجع الأخرى إذا طبقت رست هذه القواعد بصورةٍ حتمية ووُجد غموض ما بخصوص أي دورات الحياة تنتمي للمراجع، ففي هذه الحالة يعرض لك المصرف رسالة خطأ بدلًا من التخمين، ويمكنك حينها تصحيح هذا الخطأ عن طريق إضافة توصيفٍ لدورة الحياة.
</p>

<p>
	تُدعى دورات الحياة لمعاملات دالة أو تابع بدورات حياة الدخل input lifetimes بينما تُدعى دورات الحياة الخاصة بالقيم المُعادة بدورات حياة الخرج output lifetimes.
</p>

<p>
	يستخدم المصرف ثلاث قواعد لمعرفة دورات حياة المراجع عندما لا يوجد هناك توصيف مباشر لها: تُطبَّق القاعدة الأولى على دورات حياة الدخل والثانية والثالثة على دورات حياة الخرج. يتوقف المصرّف ويعطينا خطأ إذا تحقق من القواعد الثلاث ولم يتعرف على كل دورات حياة المراجع، وتنطبق هذه القواعد على تعاريف <code>fn</code> بالإضافة إلى كتل <code>impl</code>.
</p>

<p>
	تتمثل القاعدة الأولى بإسناد المصرف معامل دورة حياة لكل معامل يشكّل مرجع، بكلمات أخرى: تحصل دالةً تحتوي على معامل واحد على معامل دورة حياة واحد <code>fn foo&lt;'a&gt;(x: &amp;'a i32)‎</code>، بينما تحصل دالة تحتوي على معاملين على دورتَي حياة منفصلتين <code>foo&lt;'a, 'b&gt;(x: &amp;'a i32, y: &amp;'b i32)‎</code>، وهلمّ جرًا.
</p>

<p>
	تنص القاعدة الثانية على وجود معامل دورة حياة دخل واحد فقط، وتُسند دورة الحياة هذه إلى جميع معاملات دورة حياة الخرج كالآتي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1782_7" style=""><span class="pln"> fn foo&lt;'a&gt;(x: &amp;'a i32) -&gt; &amp;'a i32</span></pre>

<p>
	أخيرًا، تنص القاعدة الثالثة على إسناد دورة الحياة الخاصة بـ <code>self</code> لجميع معاملات دورة حياة الخرج، إذا وُجدت عدّة معاملات دورة حياة دخل وكان أحدها <code>‎&amp;self</code> أو <code>‎&amp;mut self</code> لأنها تابع. تجعل القاعدة الثالثة من <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D9%88%D8%A7%D8%A8%D8%B9-methods-%D8%B6%D9%85%D9%86-%D8%A7%D9%84%D9%87%D9%8A%D8%A7%D9%83%D9%84-structs-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1850/" rel="">التوابع</a> أسهل قراءةً لأنها تُغنينا عن استخدام الكثير من الرموز في تعريفها.
</p>

<p>
	لنفترض أننا المصرّف. دعنا نطبّق هذه القواعد لمعرفة دورات حياة المراجع في بصمة الدالة <code>first_word</code> في الشيفرة 25. تبدأ بصمة الدالة دون أي دورات حياة مرتبطة بالمراجع:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8971_37" style=""><span class="pln">fn first_word</span><span class="pun">(</span><span class="pln">s</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">str </span><span class="pun">{</span></pre>

<p>
	يطبّق المصرف القاعدة الأولى التي تقتضي بأن كل معامل سيحصل على دورة حياة خاصة بها، دعنا نسمّي دورة الحياة باسم <code>‎'a</code> كالمعتاد. أصبحت لدينا بصمة الدالة بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8971_39" style=""><span class="pln">fn first_word</span><span class="pun">&lt;</span><span class="str">'a&gt;(s: &amp;'</span><span class="pln">a str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">str </span><span class="pun">{</span></pre>

<p>
	نطبق القاعدة الثانية لوجود دورة حياة دخل واحدة، وتحدِّد القاعدة الثانية أن دورة حياة معامل الداخل تُسند إلى دورة حياة الخرج، فتصبح بصمة الدالة كما يلي:
</p>

<pre class="ipsCode">fn first_word&lt;'a&gt;(s: &amp;'a str) -&gt; &amp;'a str {
</pre>

<p>
	أصبح الآن لجميع المراجع الموجودة في بصمة الدالة دورة حياة، ويمكن للمصرف أن يستمرّ بتحليله دون حاجة المبرمج لتوصيف دورات الحياة في بصمة الدالة.
</p>

<p>
	دعنا ننظر إلى مثال آخر، نستخدم هذه المرة الدالة <code>longest</code> التي لا تحتوي على معاملات دورة حياة عندما بدأنا بكتابتها في الشيفرة 20 سابقًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8971_41" style=""><span class="pln">fn longest</span><span class="pun">(</span><span class="pln">x</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">str</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">str </span><span class="pun">{</span></pre>

<p>
	نطبّق القاعدة الأولى: يحصل كل معامل على دورة حياة خاصة به. لدينا الآن في هذه الحالة معاملين بدلًا من واحد، لذا سنحصل على دورتين حياة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8971_43" style=""><span class="pln">fn longest</span><span class="pun">&lt;</span><span class="str">'a, '</span><span class="pln">b</span><span class="pun">&gt;(</span><span class="pln">x</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="str">'a str, y: &amp;'</span><span class="pln">b str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">str </span><span class="pun">{</span></pre>

<p>
	يمكنك رؤية أن القاعدة الثانية لا تنطبق على هذه الحالة لوجود أكثر من دورة حياة دخل واحدة، كما أن القاعدة الثالثة لا تنطبق لأن <code>longest</code> دالة وليست تابع، إذًا لا يوجد في معاملاتها <code>self</code>. لم نتوصل إلى دورة حياة النوع المُعادة بعد تطبيق القواعد الثلاث، وهذا هو السبب في حصولنا على خطأ عند محاولة تصريف الشيفرة 20، إذ أن المصرف تحقق من قواعد إخفاء دورة الحياة ولكنه لم يتعرف على جميع دورات حياة المراجع في بصمة الدالة.
</p>

<p>
	سننظر إلى دورات الحياة في سياق التوابع بما أن القاعدة الثالثة تنطبق فقط في بصمات التوابع، مما سيكشف لنا السبب في كون توصيف دورات الحياة ضمن التوابع غير مُستخدم معظم الأحيان.
</p>

<h2>
	توصيف دورة الحياة في تعاريف التابع
</h2>

<p>
	نستخدم طريقة الكتابة الخاصة بمعاملات الأنواع المعممة ذاتها عند تطبيق التوابع ضمن هياكل تحتوي على دورات حياة، إذ نصرح ونستخدم معاملات دورة الحياة بناءً على ارتباطها بحقول الهيكل أو معاملات التابع والقيم المُعادة، إذ يجب أن يُصرَّح عن أسماء دورات الحياة الخاصة بحقول الهياكل بعد الكلمة المفتاحية <code>impl</code> ومن ثم استخدامها بعد اسم الهيكل لأن دورات الحياة هذه تشكل جزءًا من نوع الهيكل.
</p>

<p>
	قد ترتبط المراجع في بصمة التابع داخل الكتلة <code>impl</code> بدورات حياة المراجع الخاصة بحقول الهيكل، وقد تكون مستقلةً عن بعضها الآخر، كما أن قوانين إخفاء دورة الحياة تجعل من توصيف دورات الحياة غير ضروري في بصمات التابع معظم الأحيان. دعنا ننظر إلى بعض الأمثلة باستخدام هيكل يدعى <code>ImportantExcerpt</code> وهو هيكل عرّفناه سابقًا في الشيفرة 24.
</p>

<p>
	لنستخدم أولًا تابعًا يدعى <code>level</code> يحتوي على معامل واحد يمثل مرجعًا إلى <code>self</code> ويُعيد قيمة من النوع <code>i32</code> (أي لا تمثّل مرجعًا):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8971_45" style=""><span class="pln">impl</span><span class="pun">&lt;</span><span class="str">'a&gt; ImportantExcerpt&lt;'</span><span class="pln">a</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn level</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> i32 </span><span class="pun">{</span><span class="pln">
        </span><span class="lit">3</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	التصريح عن معامل دورة الحياة بعد <code>impl</code> واستخدامه بعد اسم النوع مطلوب، إلا أنه من غير المطلوب توصيف دورة حياة مرجع <code>self</code> بفضل قاعدة إخفاء دورة الحياة الأولى.
</p>

<p>
	إليك مثالًا ينطبق عليه قاعدة إخفاء دورة الحياة الثالثة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8971_47" style=""><span class="pln">impl</span><span class="pun">&lt;</span><span class="str">'a&gt; ImportantExcerpt&lt;'</span><span class="pln">a</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn announce_and_return_part</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> announcement</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">str </span><span class="pun">{</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"Attention please: {}"</span><span class="pun">,</span><span class="pln"> announcement</span><span class="pun">);</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">part
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هناك دورتا حياة دخل، لذا يطبق رست القاعدة الأولى ويمنح لكل من <code>‎&amp;self</code> و <code>announcment</code> دورة حياة خاصة بهما، ومن ثم يحصل النوع المُعادة على دورة الحياة <code>‎&amp;self</code> لأن إحدى معاملات التابع قيمته <code>‎&amp;self</code>، وبهذا يجري التعرف على جميع دورات الحياة الموجودة.
</p>

<h2>
	دورة الحياة الساكنة
</h2>

<p>
	يجب أن نناقش واحدةً من دورات الحياة المميزة ألا وهي <code>static'</code> وهي تُشير إلى أن المرجع يمكن أن يعيش طوال فترة البرنامج، ولدى جميع السلاسل النصية نوع دورة الحياة الساكنة <code>static'</code>، ويمكننا توصيفه بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8971_49" style=""><span class="pln">let s</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="str">'static str = "I have a static lifetime.";</span></pre>

<p>
	يُخزّن النص الموجود في السلسلة النصية في ملف البرنامج التنفيذي مباشرةً أي أنه مرئي طوال الوقت، بالتالي فإن دورة حياة جميع السلاسل النصية المجردة literals هي <code>static'</code>.
</p>

<p>
	قد تجد اقتراحات لاستخدام دورة الحياة <code>'static</code> في رسائل الخطأ، إلا أنه يجب عليك أن تفكر فيما إذا كان المرجع بذاته يعيش طيلة دورة حياة البرنامج أم لا إذا أردت اتباع هذا الاقتراح وفيما إذا كنت تريد هذا الشيء حقًا أم لا، وتنتج رسالة الخطأ التي تقترح دورة حياة <code>static'</code> معظم الأحيان من محاولة إنشاء مرجع معلّق أو حالة عدم تطابق ما بين دورات الحياة الموجودة، وفي هذه الحالة فالحل الأمثل هو بحل هذه المشاكل وليس بتحديد دورة الحياة الساكنة <code>static'</code>.
</p>

<h2>
	معاملات الأنواع المعممة وحدود السمة ودورات الحياة معا
</h2>

<p>
	دعنا ننظر إلى طريقة تحديد معاملات الأنواع المعممة وحدود <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%B3%D9%85%D8%A7%D8%AA-traits-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1951/" rel="">السمة</a> ودورات الحياة في دالة واحدة سويًا.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8971_51" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">fmt</span><span class="pun">::</span><span class="typ">Display</span><span class="pun">;</span><span class="pln">

fn longest_with_an_announcement</span><span class="pun">&lt;</span><span class="str">'</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> T</span><span class="pun">&gt;(</span><span class="pln">
    x</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="str">'</span><span class="pln">a str</span><span class="pun">,</span><span class="pln">
    y</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="str">'</span><span class="pln">a str</span><span class="pun">,</span><span class="pln">
    ann</span><span class="pun">:</span><span class="pln"> T</span><span class="pun">,</span><span class="pln">
</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="str">'</span><span class="pln">a str
</span><span class="kwd">where</span><span class="pln">
    T</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Display</span><span class="pun">,</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"Announcement! {}"</span><span class="pun">,</span><span class="pln"> ann</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> x</span><span class="pun">.</span><span class="pln">len</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> y</span><span class="pun">.</span><span class="pln">len</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        x
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        y
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تمثل الشيفرة البرمجية السابقة دالة <code>longest</code> من الشيفرة 21 سابقًا التي تُعيد أطول شريحة نصية من شريحتين نصيتين، إلا أننا أضفنا هنا معاملًا جديدًا يدعى <code>ann</code> من نوع معمّم <code>T</code> الذي يُمكن أن يُملأ بأي نوع يطبّق السمة <code>Display</code> كما هو محدد في بنية <code>where</code>، وسيُطبع هذا المعامل الإضافي باستخدام <code>{}</code> وهذا هو السبب في جعل حدود السمة <code>Display</code> ضرورية. نكتب كل من تصاريح معاملات دورة الحياة <code>‎'a</code> ومعامل النوع المعمم <code>T</code> في القائمة ذاتها داخل الأقواس المثلثة بعد اسم الدالة وذلك لأن دورات الحياة هي نوع من الأنواع المعمّمة.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch10-00-generics.html" rel="external nofollow">Generic Types, Traits, and Lifetimes</a> من كتاب <a href="https://doc.rust-lang.org/stable/book/title-page.html" rel="external nofollow">The Rust Programming Language</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/rust/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1959/" rel="">كتابة الاختبارات في لغة رست Rust</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%B3%D9%85%D8%A7%D8%AA-traits-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1951/" rel="">السمات Traits في لغة رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D9%85%D8%A9-generic-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-rust-r1935/" rel="">مقدمة إلى مفهوم الأنواع المعممة Generic Types في لغة Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%87%D9%8A%D8%A7%D9%83%D9%84-structs-%D9%84%D8%AA%D9%86%D8%B8%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1849/" rel="">استخدام الهياكل structs لتنظيم البيانات في لغة رست Rust</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1952</guid><pubDate>Tue, 11 Apr 2023 15:06:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x633;&#x645;&#x627;&#x62A; Traits &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A; Rust</title><link>https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%B3%D9%85%D8%A7%D8%AA-traits-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1951/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_04/-Traits----Rust.jpg.193e093cd2167269ec32176ef52296c8.jpg" /></p>
<p>
	يمكن أن تعرّف السمة وظيفة نوع محدد ويمكن مشاركتها مع عدّة أنواع، ويمكننا استخدام السمات لتعريف سلوك مشترك بطريقة مجردة، ويمكننا استخدام حدود السمة trait bounds لتحديد أن النوع المعمّم يمكن أن يكون أي نوع يمتلك سلوكًا محددًا.
</p>

<p>
	<strong>ملاحظة</strong>: السمات مشابهة لميزة تدعى الواجهات interfaces في <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغات برمجة</a> أخرى، إلا أن هناك بعض الاختلافات.
</p>

<h2>
	تعريف سمة Trait
</h2>

<p>
	يتكون سلوك النوع من توابع يمكننا استدعائها على هذا النوع، ونقول أن عدّة أنواع تشارك السلوك ذاته إذا أمكننا استدعاء التوابع ذاتها على جميع هذه الأنواع، ويُعد تعريف السمة طريقةً لجمع بصمات التوابع method signatures لتعريف مجموعة من السلوكيات المهمة لتحقيق غرض ما.
</p>

<p>
	على سبيل المثال، دعنا نفترض وجود عدّة <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%87%D9%8A%D8%A7%D9%83%D9%84-structs-%D9%84%D8%AA%D9%86%D8%B8%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1849/" rel="">هياكل</a> تحمل أنواع وكميات مختلفة من النص، إذ يحمل الهيكل <code>NewsArticle</code> حقلًا لمحتوى إخباري في موقع معين، ويمكن أن تحتوي <code>Tweet</code> على نص طوله 280 محرفًا بالحد الأعظمي، إضافةً إلى البيانات الوصفية metadata التي تشير إلى كون التغريدة جديدة، أو إعادة تغريد retweet، أو رد على تغريدة أخرى.
</p>

<p>
	نريد أن نُنشئ وحدة مكتبة مصرَّفة library crate تجمع الأخبار تدعى <code>aggregator</code>، بحيث تعرض ملخصًا <a href="https://academy.hsoub.com/programming/rust/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-data-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1780/" rel="">للبيانات</a> التي قد تجدها في نسخ <code>NewsArticle</code> أو <code>Tweet</code>، ثمّ سنستدعي الملخص لأي من النسخ باستدعاء التابع <code>summarize</code>. توضح الشيفرة 12 تعريف السمة العامة <code>Summary</code> التي تعبّر عن هذا السلوك.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8687_18" style=""><span class="pln">pub trait </span><span class="typ">Summary</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn summarize</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">String</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 12: سمة <code>Summary</code> تتألف من السلوك الموجود في التابع <code>summarize</code>
</p>

<p>
	نصرّح هنا عن سمة باستخدام الكلمة المفتاحية <code>trait</code> متبوعةً باسم السمة وهي <code>Summary</code> في هذه الحالة، كما نصرّح أيضًا عن السمة بكونها عامة <code>pub</code> بحيث تستخدم الوحدات المصرّفة هذه الوحدة المصرفة كما سنرى في الأمثلة القادمة. نصرّح عن بصمات التابع داخل القوسين المعقوصين curly brackets، إذ تصف البصمات سلوك الأنواع التي تطبق هذه السمة، والتي هي في هذه الحالة <code>fn summarize(&amp;self) -&gt; String</code>.
</p>

<p>
	بعد التصريح عن بصمة التابع، يمكننا استخدام الفاصلة المنقوطة بدلًا من الأقواس المعقوصة. ويجب على كل نوع ينفّذ هذه السمة أن يوفّر سلوكه المخصص لمتن التابع. سيفرض المصرّف أن أي نوع له السمة <code>Summary</code> سيكون له تابع باسم <code>summarize</code> مُعرّف بتلك البصمة المُحدَّدة.
</p>

<p>
	يمكن أن تحتوي السمة عدّة <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D9%88%D8%A7%D8%A8%D8%B9-methods-%D8%B6%D9%85%D9%86-%D8%A7%D9%84%D9%87%D9%8A%D8%A7%D9%83%D9%84-structs-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1850/" rel="">توابع</a> في متنها، إذ أن بصمات التوابع محتواة في كل سطر على حدة، وينتهي كل سطر بفاصلة منقوطة.
</p>

<h2>
	تطبيق السمة على نوع
</h2>

<p>
	الآن وبعد أن عرَّفنا البصمات المطلوبة لتوابع السمة <code>Summary</code> يمكننا تطبيقها على الأنواع الموجودة في مجمّع الوسائط media aggregator. توضح الشيفرة 13 تنفيذًا للسمة <code>Summary</code> في الهيكل <code>NewsArticle</code> الذي يستخدم كل من العنوان والمؤلف والمكان لإنشاء قيمة مُعادة من <code>summarize</code>. نعرّف من أجل الهيكل <code>Tweet</code> الدالة <code>summarize</code> بحيث تحصل على اسم المستخدم متبوعًا بالنص الكامل الموجود في التغريدة وذلك بفرض أن التغريدة محدودة بمقدار 280 محرف.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8687_20" style=""><span class="pln">pub </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">NewsArticle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pub headline</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
    pub location</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
    pub author</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
    pub content</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

impl </span><span class="typ">Summary</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">NewsArticle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn summarize</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        format</span><span class="pun">!(</span><span class="str">"{}, by {} ({})"</span><span class="pun">,</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">headline</span><span class="pun">,</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">author</span><span class="pun">,</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">location</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

pub </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Tweet</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pub username</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
    pub content</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
    pub reply</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">bool</span><span class="pun">,</span><span class="pln">
    pub retweet</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">bool</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

impl </span><span class="typ">Summary</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">Tweet</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn summarize</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        format</span><span class="pun">!(</span><span class="str">"{}: {}"</span><span class="pun">,</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">username</span><span class="pun">,</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">content</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 13: تطبيق السمة <code>Summary</code> على كل من النوعين <code>NewsArticle</code> و <code>Tweet</code>
</p>

<p>
	تطبيق سمة على نوع هي عملية مشابهة لتطبيق توابع اعتيادية، إلا أن الفارق هنا هو أننا نضع اسم السمة التي نريد تطبيقها بعد <code>impl</code>، ثم نستخدم الكلمة المفتاحية <code>for</code> ونحدد اسم النوع الذي نريد تطبيق السمة عليه. نضع داخل كتلة <code>impl</code> بصمات التابع المعرفة في تعريف السمة، وبدلًا من إضافة الفاصلة المنقوطة بعد كل بصمة سنستخدم الأقواس المعقوصة ونملأ داخلها متن التابع مع السلوك المخصص الذي نريد من توابع السمة أن تمتلكه لنوع معين.
</p>

<p>
	الآن وبعد أن طبّقنا السمة <code>Summary</code> في وحدة المكتبة المصرفة على <code>NewsArtilce</code> و <code>Tweet</code>، يمكن لمستخدمي الوحدة المصرّفة استدعاء توابع السمة على نسخٍ من <code>NewsArticle</code> و <code>Tweet</code> بالطريقة ذاتها التي نستدعي بها توابع اعتيادية، إلا أن الفارق الوحيد هنا هو أن المستخدم يجب أن يُضيف السمة إلى النطاق scope إضافةً إلى الأنواع. إليك مثالًا عن كيفية استخدام وحدة المكتبة المصرفة <code>aggregator</code> من قِبل وحدة ثنائية مصرّفة binary crate:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8687_22" style=""><span class="pln">use aggregator</span><span class="pun">::{</span><span class="typ">Summary</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Tweet</span><span class="pun">};</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let tweet </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tweet</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        username</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"horse_ebooks"</span><span class="pun">),</span><span class="pln">
        content</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="pln">
            </span><span class="str">"of course, as you probably already know, people"</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">),</span><span class="pln">
        reply</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
        retweet</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">

    println</span><span class="pun">!(</span><span class="str">"1 new tweet: {}"</span><span class="pun">,</span><span class="pln"> tweet</span><span class="pun">.</span><span class="pln">summarize</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تطبع الشيفرة البرمجية السابقة ما يلي:
</p>

<pre class="ipsCode">1 new tweet: horse_ebooks: of course, as you probably already know, people
</pre>

<p>
	يُمكن أن تضيف الوحدات المصرّفة الأخرى المعتمدة على الوحدة المصرفة <code>aggregator</code> السمة <code>Summary</code> إلى النطاق لتطبيق <code>Summary</code> على أنواعها الخاصة، إلا أن القيد الوحيد هنا الذي يجب ملاحظته هو أنه يمكننا تطبيق السمة على نوع نريده فقط إذا كانت سمة واحدة على الأقل أو نوعًا واحدًا على الأقل محليًا local بالنسبة لوحدتنا المصرّفة؛ إذ يمكننا على سبيل المثال تطبيق سمات المكتبة القياسية مثل <code>Display</code> على نوع مخصص مثل <code>Tweet</code> بمثابة جزء من وظيفة وحدتنا المصرفة <code>aggregator</code>، لأن النوع <code>Tweet</code> هو محلي بالنسبة إلى الوحدة المصرفة <code>aggregator</code>، كما يمكننا أيضًا تطبيق <code>Summary</code> على النوع <code>Vec&lt;T&gt;‎</code> في الوحدة المصرفة <code>aggregator</code> لأن السمة <code>Summary</code> هي سمة محلية بالنسبة لوحدتنا المصرفة <code>aggregator</code>.
</p>

<p>
	في المقابل، لا يمكننا تطبيق سمة خارجية على أنواع خارجية، فعلى سبيل المثال لا يمكننا تطبيق السمة <code>Display</code> على النوع <code>Vec&lt;T&gt;‎</code> داخل الوحدة المصرفة <code>aggregator</code>، لأن <code>Display</code> و <code>Vec&lt;T&gt;‎</code> ليستا معرفتين في المكتبة القياسية أو محليةً بالنسبة للوحدة المصرفة <code>aggregator</code>. يُعد هذا القيد جزءًا من خاصية تدعى <strong>الترابط المنطقي coherence</strong> وبالأخص قاعدة اليتيم orphan rule وتسمى القاعدة بهذا الاسم لأن نوع الأب غير موجود، وتتأكد هذه القاعدة من أن الشيفرة البرمجية الخاصة بالمبرمجين الآخرين لن تتسبب بعطل شيفرتك البرمجية والعكس صحيح، وبدون هذه القاعدة يمكن للوحدتين المصرفتين تطبيق السمة ذاتها على النوع ذاته، وعندها لن تستطيع رست معرفة أي من التنفيذين يجب استخدامه.
</p>

<h2>
	التنفيذات الافتراضية
</h2>

<p>
	من المفيد في بعض الأحيان تواجد سلوك افتراضي لبعض التوابع الموجودة في سمة ما أو جميعها بدلًا من طلب كتابة متن لكل التوابع ضمن كل نوع، بحيث يمكننا إعادة الكتابة على السلوك الافتراضي للتابع إذا أردنا تطبيق السمة على نوع معيّن.
</p>

<p>
	نحدد في الشيفرة 14 سلسلةً نصيةً افتراضية للتابع <code>summarize</code> ضمن السمة <code>Summary</code> بدلًا من تعريف بصمة التابع كما فعلنا في الشيفرة 12.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8687_24" style=""><span class="pln">pub trait </span><span class="typ">Summary</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn summarize</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"(Read more...)"</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 14: تعريف سمة <code>Summary</code> بتنفيذ افتراضي خاص بالتابع <code>summarize</code>
</p>

<p>
	نحدد كتلة <code>impl</code> فارغة بكتابة <code>impl Summary for NewsArticle {}‎</code> لاستخدام التنفيذ الافتراضي لتلخيص نسخ <code>NewsArticle</code>.
</p>

<p>
	على الرغم من أننا لا نعرف بعد الآن التابع <code>summarize</code> على <code>NewsArticle</code> مباشرةً إلا أننا قدمنا متنًا افتراضيًا وحددنا أن <code>NewsArticle</code> تستخدم السمة <code>Summary</code>، ونتيجةً لذلك يمكننا استدعاء التابع <code>summarize</code> على نسخة من <code>NewsArticle</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8687_26" style=""><span class="pln">    let article </span><span class="pun">=</span><span class="pln"> </span><span class="typ">NewsArticle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        headline</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"Penguins win the Stanley Cup Championship!"</span><span class="pun">),</span><span class="pln">
        location</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"Pittsburgh, PA, USA"</span><span class="pun">),</span><span class="pln">
        author</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"Iceburgh"</span><span class="pun">),</span><span class="pln">
        content</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="pln">
            </span><span class="str">"</span><span class="typ">The</span><span class="pln"> </span><span class="typ">Pittsburgh</span><span class="pln"> </span><span class="typ">Penguins</span><span class="pln"> once again are the best \
             hockey team in the NHL</span><span class="pun">.</span><span class="str">",</span><span class="pln">
        </span><span class="pun">),</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">

    println</span><span class="pun">!(</span><span class="str">"New article available! {}"</span><span class="pun">,</span><span class="pln"> article</span><span class="pun">.</span><span class="pln">summarize</span><span class="pun">());</span></pre>

<p>
	تطبع الشيفرة البرمجية السابقة ما يلي:
</p>

<pre class="ipsCode">New article available! (Read more...)‎
</pre>

<p>
	لا يتطلب إنشاء تنفيذ افتراضي تعديل أي شيء بخصوص تنفيذ <code>Summary</code> على <code>Tweet</code> في الشيفرة 13، وذلك لأن طريقة الكتابة على التنفيذ الافتراضي مماثلة لصيغة تنفيذ تابع سمة لا يحتوي على تنفيذ افتراضي.
</p>

<p>
	يمكن أن تستدعي التنفيذات الافتراضية توابع أخرى في السمة ذاتها حتى لو كانت التوابع الأخرى لا تحتوي على تنفيذ افتراضي، وبذلك يمكن أن تقدم السمة الكثير من المزايا المفيدة باستخدامها لتنفيذٍ محدد في جزء صغير منها، على سبيل المثال يمكننا أن نعرف السمة <code>Summary</code> بحيث تحتوي على تابع <code>summarize_author</code> يحتوي على تنفيذ داخله ومن ثم تابع <code>summarize</code> يحتوي على تنفيذٍ افتراضي يستدعي التابع <code>summarize_author</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8687_28" style=""><span class="pln">pub trait </span><span class="typ">Summary</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn summarize_author</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">String</span><span class="pun">;</span><span class="pln">

    fn summarize</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        format</span><span class="pun">!(</span><span class="str">"(Read more from {}...)"</span><span class="pun">,</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">summarize_author</span><span class="pun">())</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لاستخدام هذا الإصدار من <code>Summary</code> علينا أن نعرف <code>summarize_author</code> عند تطبيق السمة على النوع:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8687_30" style=""><span class="pln">impl </span><span class="typ">Summary</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">Tweet</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn summarize_author</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        format</span><span class="pun">!(</span><span class="str">"@{}"</span><span class="pun">,</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">username</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكننا استدعاء <code>summarize</code> على نسخة من هيكل <code>Tweet</code> بعد تعريفنا التابع <code>summarize_author</code>، وعندها سيستدعي التنفيذ الافتراضي للتابع <code>summarize</code> تعريف التابع <code>summarize_author</code> الذي أضفناه، ولأننا كتبنا <code>summarize_author</code> فنحن منحنا للسمة <code>Summary</code> سلوكًا للتابع <code>summarize</code> دون كتابة المزيد من الأسطر البرمجية.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8687_32" style=""><span class="pln">    let tweet </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tweet</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        username</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"horse_ebooks"</span><span class="pun">),</span><span class="pln">
        content</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="pln">
            </span><span class="str">"of course, as you probably already know, people"</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">),</span><span class="pln">
        reply</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
        retweet</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">

    println</span><span class="pun">!(</span><span class="str">"1 new tweet: {}"</span><span class="pun">,</span><span class="pln"> tweet</span><span class="pun">.</span><span class="pln">summarize</span><span class="pun">());</span></pre>

<p>
	تطبع الشيفرة البرمجية السابقة ما يلي:
</p>

<pre class="ipsCode">1 new tweet: (Read more from @horse_ebooks...)‎
</pre>

<p>
	لاحظ أنه ليس من الممكن استدعاء التنفيذ الافتراضي من تنفيذٍ كتبنا فوقه override لنفس التابع.
</p>

<h2>
	السمات مثل معاملات
</h2>

<p>
	الآن، وبعد أن تعلمنا كيفية تعريف وتطبيق السمات، أصبح بإمكاننا النظر إلى كيفية استخدام السمات لتعريف <a href="https://academy.hsoub.com/programming/rust/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-functions-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%82%D8%A7%D8%AA-comments-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%A7%D8%B3%D8%AA-rust-r1781/" rel="">الدوال</a> التي تقبل العديد من الأنواع المختلفة، وسنستخدم هنا السمة <code>Summary</code> التي طبقناها على النوعين <code>NewsArtilce</code> و <code>Tweet</code> في الشيفرة 13 لتعريف الدالة <code>notify</code> التي تستدعي التابع <code>summarize</code> على المعامل <code>item</code> وهو نوع ينفّذ السمة <code>Summary</code>. لتحقيق ذلك علينا أن نكتب صيغة <code>impl Trait</code> بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8687_34" style=""><span class="pln">pub fn notify</span><span class="pun">(</span><span class="pln">item</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">impl </span><span class="typ">Summary</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"Breaking news! {}"</span><span class="pun">,</span><span class="pln"> item</span><span class="pun">.</span><span class="pln">summarize</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	بدلًا من استخدام نوع ثابت للمعامل <code>item</code> نحدد الكلمة المفتاحية <code>impl</code> ومن ثم اسم السمة، إذ يقبل هذا المعامل أي نوع ينفّذ السمة التي حددناها. يمكننا استدعاء أي تابع في <code>notify</code> على <code>item</code> يحتوي على السمة <code>Summary</code> مثل <code>summarize</code>، إذ يمكننا استدعاء <code>notify</code> وتمرير أي نسخة من <code>NewsArticle</code> أو <code>Tweet</code>. لن تصرَّف الشيفرة البرمجية التي تستدعي الدالة باستخدام نوع آخر مثل <code>String</code> أو <code>i32</code> وذلك لأن الأنواع هذه لا تنفّذ <code>Summary</code>.
</p>

<h3>
	صيغة حدود السمة
</h3>

<p>
	تكون صيغة <code>impl Triat</code> جيدة للاستخدامات البسيطة، إلا أنها طريقة مختصرة عن طريقة أطول تُعرف بحدود السمة trait bound، وتبدو على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8687_36" style=""><span class="pln">pub fn notify</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Summary</span><span class="pun">&gt;(</span><span class="pln">item</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">T</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"Breaking news! {}"</span><span class="pun">,</span><span class="pln"> item</span><span class="pun">.</span><span class="pln">summarize</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تماثل هذه الكتابة الطويلة الكتابة في القسم السابق إلا أنها أطول، إذ أننا نضع حدود السمة في تصريح معاملات <a href="https://academy.hsoub.com/programming/rust/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D9%85%D8%A9-generic-data-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-rust-r1936/" rel="">النوع المعمم</a> بعد النقطتين وداخل أقواس مثلثة angle brackets.
</p>

<p>
	تُعد صيغة <code>impl Trait</code> مناسبة وتجعل من شيفرتنا البرمجية أبسط في العديد من الحالات البسيطة إلا أن كتابة حدود السمة بشكلها الكامل تسمح لنا بتحديد تفاصيل أدق في بعض الحالات، على سبيل المثال يمكننا كتابة معاملين ينفّذان السمة <code>Summary</code> وكتابة هذا الأمر بصيغة <code>impl Trait</code>، وسيبدو بهذا الشكل:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8687_38" style=""><span class="pln">pub fn notify</span><span class="pun">(</span><span class="pln">item1</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">impl </span><span class="typ">Summary</span><span class="pun">,</span><span class="pln"> item2</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">impl </span><span class="typ">Summary</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span></pre>

<p>
	يُعد استخدام صيغة <code>impl Trait</code> ملائمًا إذا أردنا لهذه الدالة السماح للمعاملين <code>item1</code> و <code>item2</code> أن يكونا من نوعين مختلفين (طالما ينفّذ كلاهما <code>Summary</code>). إذا أردنا إجبار المعاملين على استخدام النوع ذاته يجب أن نستخدم حدود السمة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8687_40" style=""><span class="pln">pub fn notify</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Summary</span><span class="pun">&gt;(</span><span class="pln">item1</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">T</span><span class="pun">,</span><span class="pln"> item2</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">T</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span></pre>

<p>
	يقيّد النوع المعمّم <code>T</code> المحدد على أنه نوع لكل من المعاملين <code>item1</code> و <code>item2</code> الدالة بأنه يجب عليها قبول القيمتين فقط إذا كان كل من <code>item1</code> و <code>item2</code> لهما النوع ذاته.
</p>

<h3>
	تحديد حدود سمة عديدة باستخدام صيغة +
</h3>

<p>
	يمكننا تحديد أكثر من حد سمة واحد، لنقل أننا نريد <code>notify</code> أن تستخدم تنسيق طباعة معيّن بالإضافة إلى <code>summarize</code> على <code>item</code>، عندها نحدد في تعريف <code>notify</code> أنه يجب على <code>item</code> أن تنفّذ كلًا من <code>Display</code> و <code>Summary</code> بنفس الوقت، ويمكننا فعل ذلك باستخدام الصيغة <code>+</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8687_42" style=""><span class="pln">pub fn notify</span><span class="pun">(</span><span class="pln">item</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;(</span><span class="pln">impl </span><span class="typ">Summary</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="typ">Display</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span></pre>

<p>
	الصيغة <code>+</code> صالحة أيضًا مع حدود السمات على الأنواع المعممة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8687_44" style=""><span class="pln">pub fn notify</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Summary</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="typ">Display</span><span class="pun">&gt;(</span><span class="pln">item</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">T</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span></pre>

<p>
	يمكن لمتن الدالة <code>notify</code> أن يستدعي <code>summarize</code> مع استخدام <code>{}</code> لتنسيق <code>item</code> وذلك مع وجود حدّين للسمة.
</p>

<h3>
	حدود سمة أوضح باستخدام بنى where
</h3>

<p>
	لاستخدام حدود سمة عديدة بعض السلبيات إذ أن كل نوع معمم يحتوي على حد سمة خاص به، لذا من الممكن للدوال التي تحتوي على عدة أنواع معممة مثل معاملات أن تحتوي الكثير من المعلومات بخصوص حدود السمة بين اسم الدالة ولائحة معاملاتها مما يجعل بصمة الدالة صعبة القراءة، ولذلك تحتوي رست على طريقة كتابة بديلة لتحديد حدود السمة داخل بنية <code>where</code> بعد بصمة الدالة، وبالتالي يمكننا استخدام البنية <code>where</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8687_46" style=""><span class="pln">fn some_function</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">,</span><span class="pln"> U</span><span class="pun">&gt;(</span><span class="pln">t</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">T</span><span class="pun">,</span><span class="pln"> u</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">U</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> i32
</span><span class="kwd">where</span><span class="pln">
    T</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Display</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="typ">Clone</span><span class="pun">,</span><span class="pln">
    U</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Clone</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="typ">Debug</span><span class="pun">,</span><span class="pln">
</span><span class="pun">{</span></pre>

<p>
	بدلًا من كتابة التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8687_48" style=""><span class="pln">fn some_function</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Display</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="typ">Clone</span><span class="pun">,</span><span class="pln"> U</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Clone</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="typ">Debug</span><span class="pun">&gt;(</span><span class="pln">t</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">T</span><span class="pun">,</span><span class="pln"> u</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">U</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> i32 </span><span class="pun">{</span></pre>

<p>
	أصبحت الآن بصمة الدالة أكثر وضوحًا إذ تحتوي على اسم الدالة ولائحة معاملاتها والنوع الذي تُعيده على سطر واحد بصورةٍ مشابهة لدالة لا تحتوي على الكثير من حدود السمة.
</p>

<h2>
	إعادة الأنواع التي تنفذ السمات
</h2>

<p>
	يمكننا أيضًا استخدام صيغة <code>impl Triat</code> في مكان الإعادة لإعادة قيمة من نوع ما يطبّق سمة، كما هو موضح هنا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8687_50" style=""><span class="pln">fn returns_summarizable</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> impl </span><span class="typ">Summary</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Tweet</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        username</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"horse_ebooks"</span><span class="pun">),</span><span class="pln">
        content</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="pln">
            </span><span class="str">"of course, as you probably already know, people"</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">),</span><span class="pln">
        reply</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
        retweet</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نستطيع تحديد أن الدالة <code>returns_summarizable</code> تُعيد نوعًا يطبق السمة <code>Summary</code> باستخدام <code>impl Summary</code> على أنه نوع مُعاد دون تسمية النوع الثابت، وفي هذه الحالة تعيد الدالة <code>returns_summarizable</code> القيمة <code>Tweet</code> إلا أنه ليس من الضروري أن تعلم الشيفرة التي تستدعي الدالة بذلك.
</p>

<p>
	إمكانية تحديد قيمة مُعادة فقط عن طريق السمة التي تطبقها مفيد جدًا، بالأخص في سياق <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%85%D9%86%D8%BA%D9%84%D9%82%D8%A7%D8%AA-closure-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r872/" rel="">المغلفات closures</a> والمكررات iterators وهما مفهومان سنتكلم عنهما لاحقًا، إذ تُنشئ المغلفات والمكررات أنواعًا يعرفها المصرف فقط، أو أنواعًا يتطلب تحديدها كتابةً طويلةً إلا أن الصيغة <code>impl Trait</code> تسمح لك بتحديد أن الدالة تُعيد نوعًا ما يطبّق السمة <code>Iterator</code> دون الحاجة لكتابة نوع طويل.
</p>

<p>
	يمكنك استخدام <code>impl Trait</code> فقط في حال إعادتك لنوع واحد، على سبيل المثال تُعيد الشيفرة البرمجية التالية إما <code>NewsArticle</code>، أو <code>Tweet</code> بتحديد النوع المُعاد باستخدام <code>impl Summary</code> إلا أن ذلك لا ينجح:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8687_52" style=""><span class="pln">fn returns_summarizable</span><span class="pun">(</span><span class="kwd">switch</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">bool</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> impl </span><span class="typ">Summary</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="kwd">switch</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">NewsArticle</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            headline</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="pln">
                </span><span class="str">"Penguins win the Stanley Cup Championship!"</span><span class="pun">,</span><span class="pln">
            </span><span class="pun">),</span><span class="pln">
            location</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"Pittsburgh, PA, USA"</span><span class="pun">),</span><span class="pln">
            author</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"Iceburgh"</span><span class="pun">),</span><span class="pln">
            content</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="pln">
                </span><span class="str">"</span><span class="typ">The</span><span class="pln"> </span><span class="typ">Pittsburgh</span><span class="pln"> </span><span class="typ">Penguins</span><span class="pln"> once again are the best \
                 hockey team in the NHL</span><span class="pun">.</span><span class="str">",</span><span class="pln">
            </span><span class="pun">),</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Tweet</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            username</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"horse_ebooks"</span><span class="pun">),</span><span class="pln">
            content</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="pln">
                </span><span class="str">"of course, as you probably already know, people"</span><span class="pun">,</span><span class="pln">
            </span><span class="pun">),</span><span class="pln">
            reply</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
            retweet</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إعادة إما <code>NewsArticle</code> أو <code>Tweet</code> ليس مسموحًا بسبب القيود التي يفرضها استخدام الصيغة <code>impl Trait</code> وكيفية تنفيذها في المصرّف، وسنتكلم لاحقًا عن كيفية كتابة دالة تحقق هذا السلوك لاحقًا.
</p>

<h2>
	استخدام حدود السمة لتنفيذ التوابع شرطيا
</h2>

<p>
	يمكننا تنفيذ التوابع شرطيًا للأنواع التي تنفّذ سمةً ما عند استخدام هذه السمة بواسطة كتلة <code>impl</code> التي تستخدم الأنواع المعممة مثل معاملات. على سبيل المثال، ينفّذ النوع <code>Pair&lt;T&gt;‎</code> في الشيفرة 15 الدالة <code>new</code> دومًا لإعادة نسخةٍ جديدة من <code>Pair&lt;T&gt;‎</code> (تذكر أن <code>self</code> هو اسم نوع مستعار للنوع الموجود في الكتلة <code>impl</code> وهو <code>Pair&lt;T&gt;‎</code> في هذه الحالة)، إلا أنه في كتلة <code>impl</code> التالية ينفّذ <code>Pair&lt;T&gt;‎</code> التابع <code>cmp_display</code> فقط إذا كان النوع <code>T</code> الداخلي ينفّذ السمة <code>PartialOrd</code> التي تمكّن المقارنة بالإضافة إلى سمة `Display التي تمكّن الطباعة.
</p>

<p>
	اسم الملف: src/lib.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8687_54" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">fmt</span><span class="pun">::</span><span class="typ">Display</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Pair</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    x</span><span class="pun">:</span><span class="pln"> T</span><span class="pun">,</span><span class="pln">
    y</span><span class="pun">:</span><span class="pln"> T</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

impl</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> </span><span class="typ">Pair</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn </span><span class="kwd">new</span><span class="pun">(</span><span class="pln">x</span><span class="pun">:</span><span class="pln"> T</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> T</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Self</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Self</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> y </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

impl</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Display</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="typ">PartialOrd</span><span class="pun">&gt;</span><span class="pln"> </span><span class="typ">Pair</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn cmp_display</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">x </span><span class="pun">&gt;=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">y </span><span class="pun">{</span><span class="pln">
            println</span><span class="pun">!(</span><span class="str">"The largest member is x = {}"</span><span class="pun">,</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">x</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            println</span><span class="pun">!(</span><span class="str">"The largest member is y = {}"</span><span class="pun">,</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">y</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 15: تنفيذ توابع شرطيًا على نوع معمّم بحسب حدود السمة
</p>

<p>
	يمكننا أيضًا تنفيذ سمة شرطيًا لأي نوع ينفّذ سمةً أخرى، وتنفيذ السمة على أي نوع يحقق حدود السمة يسمّى بالتنفيذات الشاملة blanket implementations ويُستخدم بكثرة في مكتبة رست القياسية؛ على سبيل المثال تنفِّذ المكتبة القياسية السمة <code>ToString</code> على أي نوع ينفّذ السمة <code>Display</code>، وتبدو كتلة <code>impl</code> في المكتبة القياسية بصورةٍ مشابهة لما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8687_56" style=""><span class="pln">impl</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Display</span><span class="pun">&gt;</span><span class="pln"> </span><span class="typ">ToString</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> T </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ولأن المكتبة القياسية تستخدم التنفيذ الشامل هذا فيمكننا استدعاء التابع <code>to_string</code> المعرف باستخدام السمة <code>ToString</code> على أي نوع ينفّذ السمة <code>Display</code> على سبيل المثال يمكننا تحويل الأعداد الصحيحة إلى قيمة موافقة لها في النوع <code>String</code> وذلك لأن الأعداد الصحيحة تنفّذ السمة <code>Display</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8687_58" style=""><span class="pln">let s </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3.to</span><span class="pln">_string</span><span class="pun">();</span></pre>

<p>
	يمكنك ملاحظة التنفيذات الشاملة في توثيق السمة في قسم "المنفّذين implementors".
</p>

<p>
	تسمح لنا السمات وحدود السمات بكتابة شيفرة برمجية تستخدم الأنواع المعممة مثل معاملات، وذلك للتقليل من تكرار الشيفرة البرمجية، إضافةً إلى تحديدنا للمصرف بأننا نريد لقيمة معممة أن يكون لها سلوك معين، ويمكن للمصرف عندئذ استخدام معلومات حدود السمة للتحقق من أن جميع الأنواع الثابتة المستخدمة في شيفرتنا البرمجية تحتوي على السلوك الصحيح. سنحصل في لغات البرمجة المكتوبة ديناميكيًا dynamically typed على خطأ عند وقت التشغيل runtime إذا استدعينا تابعًا على نوع لم يعرّف هذا التابع، إلا أن رست تنقل هذه <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1920/" rel="">الأخطاء</a> إلى وقت التصريف بحيث تجبرنا على تصحيح المشاكل قبل أن تُنفَّذ شيفرتنا البرمجية.
</p>

<p>
	إضافةً لما سبق، لا يتوجب علينا كتابة شيفرة برمجية تتحقق من السلوك عند وقت التشغيل لأننا تحققنا من السلوك عند وقت التصريف، ويحسّن ذلك أداء الشيفرة البرمجية دون الحاجة للتخلي عن مرونة استخدام الأنواع المعممة.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch10-00-generics.html" rel="external nofollow">Generic Types, Traits, and Lifetimes</a> من كتاب <a href="https://doc.rust-lang.org/stable/book/title-page.html" rel="external nofollow">The Rust Programming Language</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%AD%D9%82%D9%82-%D9%85%D9%86-%D8%A7%D9%84%D9%85%D8%B1%D8%A7%D8%AC%D8%B9-references-%D8%B9%D8%A8%D8%B1-%D8%AF%D9%88%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%AD%D9%8A%D8%A7%D8%A9-lifetimes-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1952/" rel="">التحقق من المراجع References عبر دورات الحياة Lifetimes في لغة رست</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D9%85%D8%A9-generic-data-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1936/" rel="">كيفية استخدام أنواع البيانات المعممة Generic Data Types في لغة رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-data-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1780/" rel="">أنواع البيانات Data Types في لغة رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AD%D8%B2%D9%85-packages-%D9%88%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B5%D8%B1%D9%81%D8%A9-crates-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1853/" rel="">الحزم packages والوحدات المصرفة crates في لغة رست Rust</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1951</guid><pubDate>Wed, 05 Apr 2023 15:00:00 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x623;&#x646;&#x648;&#x627;&#x639; &#x627;&#x644;&#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; &#x627;&#x644;&#x645;&#x639;&#x645;&#x645;&#x629; Generic Data Types &#x641;&#x64A; &#x644;&#x63A;&#x629; Rust</title><link>https://academy.hsoub.com/programming/rust/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D9%85%D8%A9-generic-data-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-rust-r1936/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_03/---Generic-Data-Types---Rust.jpg.5e99a059a86dc8b244062f7f33c2dab0.jpg" /></p>
<p>
	نستخدم الأنواع المُعمَّمة لإنشاء تعاريف لعناصر مثل بصمات الدوال function signatures أو <span ipsnoautolink="true">الهياكل structs</span>، بحيث تمكننا من استخدام عدّة أنواع بيانات ثابتة. دعنا ننظر أولًا إلى كيفية تعريف الدوال والهياكل و<span ipsnoautolink="true">المُعدّدات enums</span> والتوابع methods باستخدام الأنواع المعممة، ثم سنناقش كيف تؤثر الأنواع المعممة على أداء الشيفرة البرمجية.
</p>

<h2>
	في تعاريف الدوال
</h2>

<p>
	نضع<a href="https://academy.hsoub.com/programming/rust/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D9%85%D8%A9-generic-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-rust-r1935/" rel=""> الأنواع المعممة</a> عند تعريف دالة تستخدمها في بصمة الدالة function signature، وهو المكان الذي نحدد فيه عادةً <a href="https://academy.hsoub.com/programming/rust/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-data-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1780/" rel="">أنواع بيانات</a> المعاملات ونوع القيمة المُعادة، إذ يكسب ذلك شيفرتنا البرمجية مرونةً أكبر ويقدم مزايا أكثر للشيفرة البرمجية المُستدعية لدالتنا مع منع تكرار الشيفرة البرمجية في الوقت ذاته.
</p>

<p>
	لنستمرّ في مثال الدالة <code>largest</code> من <a href="https://academy.hsoub.com/programming/rust/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D9%85%D8%A9-generic-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1935/" rel="">المقالة السابقة</a>: توضح الشيفرة 4 دالتين يعثران على أكبر قيمة في شريحة slice ما، وسنجمع هاتين الدالتين في دالة واحدة تستخدم الأنواع المعممة.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5658_8" style=""><span class="pln">fn largest_i32</span><span class="pun">(</span><span class="typ">list</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;[</span><span class="pln">i32</span><span class="pun">])</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">i32 </span><span class="pun">{</span><span class="pln">
    let mut largest </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="typ">list</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> item in </span><span class="typ">list</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> item </span><span class="pun">&gt;</span><span class="pln"> largest </span><span class="pun">{</span><span class="pln">
            largest </span><span class="pun">=</span><span class="pln"> item</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    largest
</span><span class="pun">}</span><span class="pln">

fn largest_char</span><span class="pun">(</span><span class="typ">list</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;[</span><span class="kwd">char</span><span class="pun">])</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let mut largest </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="typ">list</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> item in </span><span class="typ">list</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> item </span><span class="pun">&gt;</span><span class="pln"> largest </span><span class="pun">{</span><span class="pln">
            largest </span><span class="pun">=</span><span class="pln"> item</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    largest
</span><span class="pun">}</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let number_list </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![</span><span class="lit">34</span><span class="pun">,</span><span class="pln"> </span><span class="lit">50</span><span class="pun">,</span><span class="pln"> </span><span class="lit">25</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">65</span><span class="pun">];</span><span class="pln">

    let result </span><span class="pun">=</span><span class="pln"> largest_i32</span><span class="pun">(&amp;</span><span class="pln">number_list</span><span class="pun">);</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"The largest number is {}"</span><span class="pun">,</span><span class="pln"> result</span><span class="pun">);</span><span class="pln">

    let char_list </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![</span><span class="str">'y'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'m'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'a'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'q'</span><span class="pun">];</span><span class="pln">

    let result </span><span class="pun">=</span><span class="pln"> largest_char</span><span class="pun">(&amp;</span><span class="pln">char_list</span><span class="pun">);</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"The largest char is {}"</span><span class="pun">,</span><span class="pln"> result</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 4: دالتان تختلفان عن بعضهما بالاسم ونوع البيانات في بصمتهما]
</p>

<p>
	الدالة <code>largest_i32</code> هي الدالة التي استخرجناها من الشيفرة 3 (في المقال السابق) التي تعثر على أكبر قيمة <code>i32</code> في شريحة، بينما تعثر الدالة <code>largest_char</code> على أكبر قيمة <code>char</code> في شريحة، ولدى الدالتين المحتوى ذاته، لذا دعنا نتخلص من التكرار باستخدام الأنواع المعممة مثل معاملات في دالة وحيدة.
</p>

<p>
	نحتاج إلى تسمية نوع المعامل حتى نكون قادرين على استخدام عدة أنواع في دالة واحدة جديدة، كما نفعل عندما نسمّي قيم معاملات الدالة، ويمكنك هنا استخدام معرّف بمثابة اسم نوع معامل، إلا أننا سنستخدم <code>T</code> لأن أسماء المعاملات في <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-%D8%A7%D9%84%D8%A8%D8%AF%D8%A7%D9%8A%D8%A7%D8%AA-r1764/" rel="">لغة رست</a> قصيرة اصطلاحًا وغالبًا ما تكون حرفًا واحدًا، كما أن اصطلاح رست في تسمية الأنواع قائمٌ على نمط سنام الجمل CamelCase، وتسمية النوع <code>T</code> هو اختصار لكلمة النوع "type" وهو الخيار الشائع لمبرمجي لغة رست.
</p>

<p>
	علينا أن نصرّح عن اسم المعامل عندما نستخدمه في متن الدالة وذلك في بصمة الدالة حتى يعرف المصرّف معنى الاسم، كما ينبغي علينا بصورةٍ مشابهة تعريف اسم نوع المعامل في بصمة الدالة قبل أن نستطيع استخدامه داخلها. لتعريف الدالة المعممة <code>largest</code> نضع تصاريح اسم النوع داخل قوسين مثلثين <code>&lt;&gt;</code> بين اسم الدالة ولائحة المعاملات بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5658_10" style=""><span class="pln">fn largest</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;(</span><span class="typ">list</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;[</span><span class="pln">T</span><span class="pun">])</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">T </span><span class="pun">{</span></pre>

<p>
	نقرأ التعريف السابق كما يلي: الدالة <code>largest</code> هي دالة معممة تستخدم نوعًا ما اسمه <code>T</code>، ولدى هذه الدالة معاملٌ واحدٌ يدعى <code>list</code> وهو قائمة من القيم نوعها <code>T</code>، وتعيد الدالة <code>largest</code> مرجعًا إلى قيمة نوعها أيضًا <code>T</code>.
</p>

<p>
	توضح الشيفرة 5 تعريف الدالة المُدمجة باستخدام نوع البيانات المعمم في بصمتها، كما توضح الشيفرة أيضًا كيفية استدعاء الدالة باستخدام شريحة من قيم <code>i32</code> أو من قيم <code>char</code>. لاحظ أن الشيفرة البرمجية لم تُصرّف بعد، إلا أننا سنصلح ذلك لاحقًا.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5658_12" style=""><span class="pln">fn largest</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;(</span><span class="typ">list</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;[</span><span class="pln">T</span><span class="pun">])</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">T </span><span class="pun">{</span><span class="pln">
    let mut largest </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="typ">list</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> item in </span><span class="typ">list</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> item </span><span class="pun">&gt;</span><span class="pln"> largest </span><span class="pun">{</span><span class="pln">
            largest </span><span class="pun">=</span><span class="pln"> item</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    largest
</span><span class="pun">}</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let number_list </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![</span><span class="lit">34</span><span class="pun">,</span><span class="pln"> </span><span class="lit">50</span><span class="pun">,</span><span class="pln"> </span><span class="lit">25</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">65</span><span class="pun">];</span><span class="pln">

    let result </span><span class="pun">=</span><span class="pln"> largest</span><span class="pun">(&amp;</span><span class="pln">number_list</span><span class="pun">);</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"The largest number is {}"</span><span class="pun">,</span><span class="pln"> result</span><span class="pun">);</span><span class="pln">

    let char_list </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![</span><span class="str">'y'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'m'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'a'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'q'</span><span class="pun">];</span><span class="pln">

    let result </span><span class="pun">=</span><span class="pln"> largest</span><span class="pun">(&amp;</span><span class="pln">char_list</span><span class="pun">);</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"The largest char is {}"</span><span class="pun">,</span><span class="pln"> result</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 5: دالة <code>largest</code> تستخدم معاملات من أنواع معممة؛ إلا أن الشيفرة لا تُصرَّف بنجاح بعد]
</p>

<p>
	إذا صرّفنا الشيفرة البرمجية السابقة، سنحصل على الخطأ التالي:
</p>

<pre class="ipsCode">$ cargo run
   Compiling chapter10 v0.1.0 (file:///projects/chapter10)
error[E0369]: binary operation `&gt;` cannot be applied to type `&amp;T`
 --&gt; src/main.rs:5:17
  |
5 |         if item &gt; largest {
  |            ---- ^ ------- &amp;T
  |            |
  |            &amp;T
  |
help: consider restricting type parameter `T`
  |
1 | fn largest&lt;T: std::cmp::PartialOrd&gt;(list: &amp;[T]) -&gt; &amp;T {
  |             ++++++++++++++++++++++

For more information about this error, try `rustc --explain E0369`.
error: could not compile `chapter10` due to previous error
</pre>

<p>
	تذكر رسالة الخطأ المساعدة <code>std::cmp::PartialOrd</code> وهي <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%B3%D9%85%D8%A7%D8%AA-traits-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1951/" rel="">سمة trait</a>، وسنتحدث عن السمات لاحقًا. يكفي معرفتك حتى اللحظة أن مفاد الخطأ هو أن محتوى الدالة <code>largest</code> لن يعمل لجميع الأنواع المحتملة للنوع <code>T</code>، وذلك لأننا نريد مقارنة قيم النوع <code>T</code> في محتوى الدالة ويمكننا الآن استخدام أنواع يمكن لقيمها أن تُرتَّب. يمكننا لتمكين المقارنات استخدام السمة <code>std::cmp::PartialOrd</code> في المكتبة القياسية على الأنواع. إذا اتبعنا النصيحة الموجودة في رسالة الخطأ فسنحدّ من الأنواع الصالحة في <code>T</code> إلى الأنواع التي تطبّق السمة <code>PartialOrd</code>، وسيُصرَّف المثال بنجاح لأن المكتبة القياسية تطبّق السمة <code>PartialOrd</code> على كلٍ من النوعين <code>i32</code> و <code>char</code>.
</p>

<h2>
	في تعاريف الهياكل
</h2>

<p>
	يمكننا أيضًا تعريف <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%87%D9%8A%D8%A7%D9%83%D9%84-structs-%D9%84%D8%AA%D9%86%D8%B8%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1849/" rel="">الهياكل</a>، بحيث تستخدم أنواع معممة مثل معامل ضمن حقل أو أكثر باستخدام <code>&lt;&gt;</code>. نعرّف في الشيفرة 6 هيكل <code>Point&lt;T&gt;‎</code> يحتوي على الحقلين <code>x</code> و <code>y</code> وهي قيم إحداثيات من أي نوع.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5658_15" style=""><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Point</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    x</span><span class="pun">:</span><span class="pln"> T</span><span class="pun">,</span><span class="pln">
    y</span><span class="pun">:</span><span class="pln"> T</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let integer </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Point</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> x</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
    let </span><span class="typ">float</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Point</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> x</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.0</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4.0</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 6: هيكل <code>Point&lt;T&gt;‎</code> يخزن بداخله القيمتين <code>x</code> و <code>y</code> نوعهما <code>T]</code>
</p>

<p>
	طريقة الكتابة في استخدام الأنواع المعممة في تعريف الهيكل مشابهة لطريقة الكتابة المستخدمة في تعاريف الدالة سابقًا، إذ نصرح أولًا عن اسم نوع المعامل داخل أقواس مثلثة بعد اسم الهيكل، ثم نستخدم النوع المعمم في تعريف الهيكل في المواضع التي نحدد فيها أنواع بيانات ثابتة في حالات أخرى.
</p>

<p>
	لاحظ أننا استخدمنا نوعًا معممًا واحدًًا فقط لتعريف <code>Point&lt;T&gt;‎</code> وبالتالي يخبرنا هذا التعريف أن الهيكل <code>Point&lt;T&gt;‎</code> هو هيكل معمم باستخدام نوع <code>T</code> وأن الحقلين <code>x</code> و <code>y</code> يحملان النوع ذاته أيًا يكن. لن تُصرَّف الشيفرة البرمجية إذا أردنا إنشاء نسخة من الهيكل <code>Point&lt;T&gt;‎</code> يحمل قيمًا من أنواع مختلفة كما نفعل في الشيفرة 7.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5658_17" style=""><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Point</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    x</span><span class="pun">:</span><span class="pln"> T</span><span class="pun">,</span><span class="pln">
    y</span><span class="pun">:</span><span class="pln"> T</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let wont_work </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Point</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> x</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4.0</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 7: يجب أن يكون للحقلين <code>x</code> و <code>y</code> النوع ذاته لأنهما يحملان النوع المعمم ذاته <code>T]</code>
</p>

<p>
	نخبر المصرف في هذا المثال عند إسنادنا القيمة العددية الصحيحة "5" إلى <code>x</code> أن النوع المعمم <code>T</code> سيكون عددًا صحيحًا لهذه النسخة من <code>Point&lt;T&gt;‎</code>. نحصل على خطأ عدم مطابقة النوع التالي عندما نحدد أن <code>y</code> قيمتها "4.0" وهي معرّفة أيضًا بحيث تحمل قيمة <code>x</code> ذاتها:
</p>

<pre class="ipsCode">$ cargo run
   Compiling chapter10 v0.1.0 (file:///projects/chapter10)
error[E0308]: mismatched types
 --&gt; src/main.rs:7:38
  |
7 |     let wont_work = Point { x: 5, y: 4.0 };
  |                                      ^^^ expected integer, found floating-point number

For more information about this error, try `rustc --explain E0308`.
error: could not compile `chapter10` due to previous error
</pre>

<p>
	نستخدم معاملات الأنواع المعممة المتعددة لتعريف الهيكل <code>Point</code> بحيث يكون كلًا من <code>x</code> و <code>y</code> من نوع معمم ولكن مختلف. على سبيل المثال، نغيّر في الشيفرة 8 تعريف <code>Point</code> لتصبح دالةً معممةً تحتوي النوعين <code>T</code> و <code>U</code>، إذ يكون نوع <code>x</code> هو <code>T</code> و <code>y</code> من النوع <code>U</code>.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5658_19" style=""><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Point</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">,</span><span class="pln"> U</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    x</span><span class="pun">:</span><span class="pln"> T</span><span class="pun">,</span><span class="pln">
    y</span><span class="pun">:</span><span class="pln"> U</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let both_integer </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Point</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> x</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
    let both_float </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Point</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> x</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.0</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4.0</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
    let integer_and_float </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Point</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> x</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4.0</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 8: دالة <code>Point&lt;T, U&gt;‎</code> المعممة التي تحتوي على نوعين بحيث يكون لكلٍ من المتغيرين <code>x</code> و <code>y</code> نوع مختلف]
</p>

<p>
	جميع نسخ <code>Point</code> الآن مسموحة، ويمكنك استخدام عدّة أنواع معممة مثل معاملات في تعريف الدالة إلا أن استخدام الكثير منها يجعل شيفرتك البرمجية صعبة القراءة. إذا احتجت كثيرًا من الأنواع المعممة في شيفرتك البرمجية فهذا يعني أنه عليك إعادة هيكلة شيفرتك البرمجية إلى أجزاء أصغر.
</p>

<h2>
	في تعاريف المعدد
</h2>

<p>
	نستطيع تعريف <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%B9%D8%AF%D8%A7%D8%AF%D8%A7%D8%AA-enums-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1851/" rel="">المعددات</a>، بحيث تحمل أنواع بيانات معممة في متغايراتها variants كما هو الأمر في الهياكل. دعنا ننظر إلى مثال آخر باستخدام المعدد <code>Option&lt;T&gt;‎</code> الموجود ضمن المكتبة القياسية الذي ناقشناه <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%B9%D8%AF%D8%A7%D8%AF%D8%A7%D8%AA-enums-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1851/" rel="">سابقًا</a>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5658_21" style=""><span class="kwd">enum</span><span class="pln"> </span><span class="typ">Option</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">T</span><span class="pun">),</span><span class="pln">
    </span><span class="typ">None</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يجب أن تفهم هذا التعريف بحلول هذه النقطة بمفردك، فكما ترى معدّد <code>Option&lt;T&gt;‎</code> هو معدد معمم يحتوي على النوع <code>T</code> ولديه متغايران: <code>Some</code> الذي يحمل قيمةً واحدةً من النوع <code>T</code> و <code>None</code> الذي لا يحمل أي قيمة. يمكننا التعبير عن المفهوم المجرّد للقيمة الاختيارية باستخدام المعدد <code>Option&lt;T&gt;‎</code>، ولأن <code>Option&lt;T&gt;‎</code> هو معدد معمم، فهذا يعني أنه يمكننا استخدامه بصورةٍ مجرّدة بغض النظر عن النوع الخاص بالقيمة الاختيارية.
</p>

<p>
	يمكن للمعددات أن تستخدم أنواعًا معممةً متعددة أيضًا، والمعدد <code>Result</code> الذي استخدمناه في مقال <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1920/" rel="">الأخطاء والتعامل معها</a> هو مثال على ذلك:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5658_23" style=""><span class="kwd">enum</span><span class="pln"> </span><span class="typ">Result</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">,</span><span class="pln"> E</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Ok</span><span class="pun">(</span><span class="pln">T</span><span class="pun">),</span><span class="pln">
    </span><span class="typ">Err</span><span class="pun">(</span><span class="pln">E</span><span class="pun">),</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	المعدد <code>Result</code> هو معدد مُعمم يحتوي على نوعين، هما: <code>T</code> و <code>E</code>، كما يحتوي على متغايرين، هما: <code>Ok</code> الذي يحمل قيمة من النوع <code>T</code> و <code>Err</code> الذي يحمل قيمة من النوع <code>E</code>، يسهّل هذا التعريف عملية استخدام المعدد <code>Result</code> في أي مكان يوجد فيه عملية قد تنجح (في هذه الحالة إعادة قيمة من نوع ما <code>T</code>)، أو قد تفشل (في هذه الحالة إعادة خطأ من قيمة ما <code>E</code>)، وهذا هو ما استخدمناه لنفتح الملف في <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1920/" rel="">مقال الأخطاء والتعامل معها في رست</a> عندما كان النوع <code>T</code> يحتوي على النوع <code>std::fs::File</code> عند فتح الملف بنجاح وكان يحتوي <code>E</code> على النوع <code>std::io::Error</code> عند ظهور مشاكل في فتح الملف.
</p>

<p>
	يمكنك اختصار حالات التكرار عندما تصادف حالات في شيفرتك البرمجية تحتوي على تعاريف هياكل ومعددات مختلفة فقط بنوع القيمة التي يحمل كل منها، وذلك عن طريق استخدام الأنواع المعممّة عوضًا عنها.
</p>

<h2>
	في تعاريف التابع
</h2>

<p>
	يمكننا تطبيق <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D9%88%D8%A7%D8%A8%D8%B9-methods-%D8%B6%D9%85%D9%86-%D8%A7%D9%84%D9%87%D9%8A%D8%A7%D9%83%D9%84-structs-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1850/" rel="">التوابع</a> على الهياكل والمعددات (كما فعلنا سابقًا في مقال <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%87%D9%8A%D8%A7%D9%83%D9%84-structs-%D9%84%D8%AA%D9%86%D8%B8%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1849/" rel=""> استخدام الهياكل structs لتنظيم البيانات</a>) واستخدام الأنواع المعممة في تعريفها أيضًا. توضح الشيفرة 9 الهيكل <code>Point&lt;T&gt;‎</code> الذي عرفناه في الشيفرة 6 مصحوبًا بتابع يدعى <code>x</code> داخله.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5658_25" style=""><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Point</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    x</span><span class="pun">:</span><span class="pln"> T</span><span class="pun">,</span><span class="pln">
    y</span><span class="pun">:</span><span class="pln"> T</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

impl</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> </span><span class="typ">Point</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn x</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">T </span><span class="pun">{</span><span class="pln">
        </span><span class="pun">&amp;</span><span class="pln">self</span><span class="pun">.</span><span class="pln">x
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let p </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Point</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> x</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10</span><span class="pln"> </span><span class="pun">};</span><span class="pln">

    println</span><span class="pun">!(</span><span class="str">"p.x = {}"</span><span class="pun">,</span><span class="pln"> p</span><span class="pun">.</span><span class="pln">x</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 9: تطبيق تابع تدعى <code>x</code> على الهيكل <code>Point&lt;T&gt;‎</code> وهو تابع يعيد مرجعًا إلى الحقل <code>x</code> الذي نوعه <code>T]</code>
</p>

<p>
	عرّفنا هنا تابعًا يدعى <code>x</code> داخل <code>Point&lt;T&gt;‎</code> يعيد <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%B1%D8%A7%D8%AC%D8%B9-references-%D9%88%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D8%A7%D8%B1%D8%A9-borrowing-%D9%88%D8%A7%D9%84%D8%B4%D8%B1%D8%A7%D8%A6%D8%AD-slices-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1787/" rel="">مرجعًا</a> إلى البيانات الموجودة في الحقل <code>x</code>. لاحظ أنه علينا التصريح عن <code>T</code> قبل <code>impl</code> حتى يتسنى لنا استخدام <code>T</code> لتحديد أننا نطبّق التوابع الموجودة في النوع <code>Point&lt;T&gt;‎</code>.
</p>

<p>
	تتعرّف رست على وجود النوع بين أقواس مثلثة في <code>Point</code> على أنه نوع معمّم وذلك بالتصريح عن <code>T</code> على أنه نوع مُعمّم بعد <code>impl</code> بدلًا عن النظر إلى النوع على أنه نوع ثابت. يمكننا اختيار اسم مختلف عن اسم معامل النوع المعمم المصرح في تعريف الهيكل لمعامل النوع المعمم هذا، إلا أن استخدام الاسم ذاته هي الطريقة الاصطلاحية. تُعرَّف التوابع المكتوبة ضمن <code>impl</code> التي تصرّح عن النوع المعمّم ضمن أي نسخة من هذا النوع بغض النظر عن النوع الثابت الذي يستبدل هذا النوع المعمم في نهاية المطاف.
</p>

<p>
	يمكننا أيضًا تحديد بعض القيود على الأنواع المعممة عند تعريف التوابع الخاصة بالنوع، فيمكننا مثلًا تطبيق تابع على نسخ <code>Point&lt;f32&gt;‎</code> فقط بدلًا من نسخ <code>Point&lt;T&gt;‎</code> التي تحتوي على أي نوع مُعمّم. نستخدم في الشيفرة 10 النوع الثابت <code>f32</code> وبالتالي لا نصرّح عن أي نوع بعد <code>impl</code>.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5658_27" style=""><span class="pln">impl </span><span class="typ">Point</span><span class="str">&lt;f32&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn distance_from_origin</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> f32 </span><span class="pun">{</span><span class="pln">
        </span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">x</span><span class="pun">.</span><span class="pln">powi</span><span class="pun">(</span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">y</span><span class="pun">.</span><span class="pln">powi</span><span class="pun">(</span><span class="lit">2</span><span class="pun">)).</span><span class="pln">sqrt</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 10: كتلة <code>impl</code> تُطبَّق فقط على هيكل بنوع ثابت معين موجود في معامل النوع المعمم <code>T]</code>
</p>

<p>
	تشير الشيفرة البرمجية السابقة إلى أن النوع <code>Point&lt;f32‎&gt;‎</code> سيتضمن التابع <code>distance_from_origin</code>، لكن لن تحتوي النسخ الأخرى من <code>Point&lt;T&gt;‎</code>، إذ تمثّل <code>T</code> نوعًا آخر ليس <code>f32</code> على تعريف هذا التابع داخلها. يقيس هذا التابع مسافة النقطة عن مبدأ الإحداثيات (0.0 ,0.0) ويستخدم عمليات حسابية متاحة فقط لأنواع قيم العدد العشري floating point.
</p>

<p>
	لا تطابق معاملات النوع المُعمم في تعريف الهيكل معاملات النوع المعمم الموجودة في بصمة الهيكل نفسه دومًا. لاحظ أننا نستخدم النوعين المعمّمين <code>X1</code> و <code>Y1</code> في الشيفرة 11 اللذين ينتميان إلى الهيكل <code>Point</code> و <code>X2</code> و <code>Y2</code> لبصمة التابع <code>mixup</code> لتوضيح المثال أكثر. تُنشئ نسخة <code>Point</code> جديدة باستخدام قيمة <code>x</code> من <code>self Point</code> (ذات النوع <code>X1</code>) وقيمة <code>y</code> من النسخة <code>Point</code> التي مرّرناها (ذات النوع <code>Y2</code>).
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5658_29" style=""><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Point</span><span class="pun">&lt;</span><span class="pln">X1</span><span class="pun">,</span><span class="pln"> Y1</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    x</span><span class="pun">:</span><span class="pln"> X1</span><span class="pun">,</span><span class="pln">
    y</span><span class="pun">:</span><span class="pln"> Y1</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

impl</span><span class="pun">&lt;</span><span class="pln">X1</span><span class="pun">,</span><span class="pln"> Y1</span><span class="pun">&gt;</span><span class="pln"> </span><span class="typ">Point</span><span class="pun">&lt;</span><span class="pln">X1</span><span class="pun">,</span><span class="pln"> Y1</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn mixup</span><span class="pun">&lt;</span><span class="pln">X2</span><span class="pun">,</span><span class="pln"> Y2</span><span class="pun">&gt;(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> other</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Point</span><span class="pun">&lt;</span><span class="pln">X2</span><span class="pun">,</span><span class="pln"> Y2</span><span class="pun">&gt;)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Point</span><span class="pun">&lt;</span><span class="pln">X1</span><span class="pun">,</span><span class="pln"> Y2</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Point</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            x</span><span class="pun">:</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">x</span><span class="pun">,</span><span class="pln">
            y</span><span class="pun">:</span><span class="pln"> other</span><span class="pun">.</span><span class="pln">y</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let p1 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Point</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> x</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10.4</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
    let p2 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Point</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> x</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Hello"</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> </span><span class="str">'c'</span><span class="pln"> </span><span class="pun">};</span><span class="pln">

    let p3 </span><span class="pun">=</span><span class="pln"> p1</span><span class="pun">.</span><span class="pln">mixup</span><span class="pun">(</span><span class="pln">p2</span><span class="pun">);</span><span class="pln">

    println</span><span class="pun">!(</span><span class="str">"p3.x = {}, p3.y = {}"</span><span class="pun">,</span><span class="pln"> p3</span><span class="pun">.</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> p3</span><span class="pun">.</span><span class="pln">y</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 11: تابع يستخدم أنواع معممة مختلفة من تعريف الهيكل]
</p>

<p>
	عرّفنا في <code>main</code> الهيكل <code>Point</code> الذي يحتوي على النوع <code>i32</code> للحقل <code>x</code> بقيمة 5، وحقل من النوع <code>f64</code> يدعى <code>y</code> بقيمة 10.4. يمثل <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%AF%D9%8A%D9%84-%D8%B9%D9%84%D9%8A%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r1779/" rel="">المتغير</a> <code>p2</code> هيكلًا من النوع <code>Point</code> يحتوي على شريحة سلسلة نصية string slice داخله في الحقل <code>x</code> بقيمة "Hello"، وقيمة من النوع <code>char</code> في الحقل <code>y</code> بقيمة c.
</p>

<p>
	يعطينا استدعاء <code>mixup</code> على النسخة <code>p1</code> باستخدام <code>p2</code> مثل معامل <code>p3</code>، وهو هيكل سيحتوي داخله على قيمة من النوع <code>i32</code> في الحقل <code>x</code> لأن <code>x</code> أتى من <code>p1</code>، وسيحتوي <code>p3</code> على حقل <code>y</code> داخله قيمة من نوع <code>char</code> لأن <code>y</code> أتى من <code>p2</code>، وبالتالي سيطبع استدعاء الماكرو <code>println!‎</code> التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5658_31" style=""><span class="pln">p3</span><span class="pun">.</span><span class="pln">x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> p3</span><span class="pun">.</span><span class="pln">y </span><span class="pun">=</span><span class="pln"> c</span></pre>

<p>
	كان الهدف من هذا المثال توضيح حالة يكون فيها المعاملات المعمّمة مصرّح عنها في <code>impl</code> وبعضها الآخر مصرّح عنها في تعريف التابع، إذ أنّ المعاملات المعممة <code>X1</code> و<code>Y1</code> مصرّحٌ عنهما هنا بعد <code>impl</code> لأنهما يندرجان تحت تعريف الهيكل، بينما تصريح المعاملين <code>X2</code> و <code>Y2</code> كان بعد <code>fn mixup</code> لأنهما متعلقان بالتابع فقط.
</p>

<h2>
	تأثير استخدام المعاملات المعممة على أداء الشيفرة البرمجية
</h2>

<p>
	قد تتسائل عمّا إذا كان هناك تراجع في أداء البرنامج عند استخدام الأنواع المعمّاة مثل معاملات، والخبر الجيد هنا أن استخدام الأنواع المعمّاة لن يجعل من البرنامج أبطأ ممّا سيكون عليه إذا استخدمت أنواعًا ثابتة.
</p>

<p>
	تنجح رست بتحقيق ذلك عن طريق إجراء عملية توحيد شكل monomorphization الشيفرة البرمجية باستخدام الأنواع المعماة وقت التصريف؛ وعملية توحيد الشكل هي عملية تحويل الشيفرة البرمجية المعممة إلى شيفرة برمجية محددة عن طريق ملئها بالأنواع الثابتة المستخدمة عند التصريف، ويعكس المصرف في هذه المرحلة ما يفعله عندما يُنشئ دالة معمّاة في الشيفرة 5؛ إذ ينظر المصرّف إلى الأماكن التي يوجد بها شيفرة برمجية معماة ويولّد شيفرة برمجية تحتوي على أنواع ثابتة تُستدعى منها الشيفرة البرمجية المعمّاة.
</p>

<p>
	دعنا ننظر إلى كيفية عمل هذه الخطوة باستخدام المعدد المعمم <code>Option&lt;T&gt;‎</code> الموجود في المكتبة القياسية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5658_33" style=""><span class="pln">let integer </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Some</span><span class="pun">(</span><span class="lit">5</span><span class="pun">);</span><span class="pln">
let </span><span class="typ">float</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Some</span><span class="pun">(</span><span class="lit">5.0</span><span class="pun">);</span></pre>

<p>
	تُجري رست عملية توحيد الشكل عندما تصرَّف الشيفرة البرمجية السابقة، ويقرأ المصرف خلال العملية القيم التي استُخدمت في نسخ <code>Option&lt;T&gt;‎</code> ويتعرف على نوعين مختلفين من <code>Option&lt;T&gt;‎</code> أحدهما <code>i32</code> والآخر <code>f64</code>، وبالتالي يتحول التعريف المعمم للنوع <code>Option&lt;T&gt;‎</code> إلى تعريفين، أحدهما تعريف للنوع <code>i32</code> والآخر للنوع <code>f64</code> ويُستبدل التعريفان بالتعريف المعمّم.
</p>

<p>
	هذا ما تبدو عليه الشيفرة البرمجية السابقة بعد إجراء عملية توحيد الشكل (يستخدم المصرف أسماءً مختلفة عمّا نستخدم هنا في المثال التوضيحي):
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5658_35" style=""><span class="kwd">enum</span><span class="pln"> </span><span class="typ">Option_i32</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">i32</span><span class="pun">),</span><span class="pln">
    </span><span class="typ">None</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">enum</span><span class="pln"> </span><span class="typ">Option_f64</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">f64</span><span class="pun">),</span><span class="pln">
    </span><span class="typ">None</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let integer </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Option_i32</span><span class="pun">::</span><span class="typ">Some</span><span class="pun">(</span><span class="lit">5</span><span class="pun">);</span><span class="pln">
    let </span><span class="typ">float</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Option_f64</span><span class="pun">::</span><span class="typ">Some</span><span class="pun">(</span><span class="lit">5.0</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُستبدل النوع المعمم <code>Option&lt;T&gt;‎</code> بتعاريف الأنواع المحددة عن طريق المصرف، ولأن رست تُصرف الشيفرة البرمجية المعممة إلى شيفرة برمجية ذات نوع ثابت لكل نسخة فلا يوجد هناك أي تراجع في أداء الشيفرة البرمجية عند استخدام الأنواع المعممة، إذ تعمل الشيفرة البرمجية عند تشغيلها بأداء مماثل لما قد يكون عليه أداء الشيفرة البرمجية التي تكرّر كل تعريف يدويًا، وتجعل عملية توحيد الشكل من الأنواع المعممة في رست ميزة فعّالة جدًا عند وقت التشغيل.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch10-00-generics.html" rel="external nofollow">Generic Types, Traits, and Lifetimes</a> من كتاب <a href="https://doc.rust-lang.org/stable/book/title-page.html" rel="external nofollow">The Rust Programming Language</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%B3%D9%85%D8%A7%D8%AA-traits-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1951/" rel="">السمات Traits في لغة رست Rust</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D9%85%D8%A9-generic-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1935/" rel="">مقدمة إلى مفهوم الأنواع المعممة Generic Types في لغة رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AD%D8%B2%D9%85-packages-%D9%88%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B5%D8%B1%D9%81%D8%A9-crates-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1853/" rel="">الحزم packages والوحدات المصرفة crates في لغة رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D9%88%D8%A7%D8%A8%D8%B9-methods-%D8%B6%D9%85%D9%86-%D8%A7%D9%84%D9%87%D9%8A%D8%A7%D9%83%D9%84-structs-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1850/" rel="">استخدام التوابع methods ضمن الهياكل structs في لغة رست Rust</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1936</guid><pubDate>Thu, 30 Mar 2023 13:02:00 +0000</pubDate></item><item><title>&#x645;&#x642;&#x62F;&#x645;&#x629; &#x625;&#x644;&#x649; &#x645;&#x641;&#x647;&#x648;&#x645; &#x627;&#x644;&#x623;&#x646;&#x648;&#x627;&#x639; &#x627;&#x644;&#x645;&#x639;&#x645;&#x645;&#x629; Generic Types &#x641;&#x64A; &#x644;&#x63A;&#x629; Rust</title><link>https://academy.hsoub.com/programming/rust/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D9%85%D8%A9-generic-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-rust-r1935/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_03/----Generic-Types---Rust.jpg.26b3d6002403d7e6197b77c22733ff63.jpg" /></p>
<p>
	تحتوي كل لغة برمجة على عدد من الأدوات للتعامل مع تكرار المفاهيم بفعالية، وتمثّل <a href="https://academy.hsoub.com/programming/java/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D9%85%D8%A9-generic-programming-r1406/" rel="">الأنواع المعممة generic types</a> في لغة رست هذه الأداة، والتي تتضمن بدائل مجرّدة لأنواع حقيقية concrete أو خاصيات أخرى. يمكننا التعبير عن سلوك الأنواع المعممة أو كيف ترتبط مع أنواع معممة أخرى دون معرفة نوع القيمة التي ستكون بداخلها عند تصريف وتشغيل الشيفرة البرمجية.
</p>

<p>
	يمكن أن تأخذ الدوال بعض الأنواع المعممة معاملات لها بدلًا من أنواع حقيقية، مثل <code>i32</code> أو <code>String</code> بطريقة مماثلة لما ستكون عليه دالة تأخذ معاملات بقيم غير معروفة لتشغيل الشيفرة البرمجية ذاتها باستخدام عدّة قيم حقيقية. استخدمنا في الحقيقة الأنواع المعممة <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%B9%D8%AF%D8%A7%D8%AF%D8%A7%D8%AA-enums-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1851/" rel="">سابقًا</a> عندما تكلمنا عن <code>Option&lt;T&gt;‎</code> وفي <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D9%84%D8%A7%D8%A6%D8%AD%D8%A9-%D9%85%D9%86-%D8%A7%D9%84%D9%82%D9%8A%D9%85-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%A3%D8%B4%D8%B9%D8%A9-vectors-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1875/" rel="">الفصل الثامن</a> عن <code>Vec&lt;T&gt;‎</code> و <code>HashMap&lt;K, V&gt;‎</code> وفي <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1920/" rel="">الفصل التاسع</a> عن <code>Result&lt;T, E&gt;‎</code> في هذه السلسلة <a href="https://academy.hsoub.com/tags/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9%20%D8%A8%D9%84%D8%BA%D8%A9%20%D8%B1%D8%B3%D8%AA/" rel="">البرمجة بلغة رست</a>. سننظر في هذا الفصل إلى كيفية تعريف نوع أو دالة أو تابع خاص بك باستخدام الأنواع المعممة.
</p>

<p>
	دعنا ننظر أولًا إلى كيفية استخراج دالة ما للتقليل من عملية تكرار الشيفرات البرمجية، ثمّ سنستخدم الطريقة ذاتها لإنشاء دالة معمّمة باستخدام دالتين مختلفتين فقط بأنواع معاملاتهما، كما سنشرح أيضًا كيفية استخدام الأنواع المعممة ضمن تعريف <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%87%D9%8A%D8%A7%D9%83%D9%84-structs-%D9%84%D8%AA%D9%86%D8%B8%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1849/" rel="">هيكل struct</a> أو تعريف <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%B9%D8%AF%D8%A7%D8%AF%D8%A7%D8%AA-enums-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1851/" rel="">مُعدّد enum</a>.
</p>

<p>
	سنتعلّم بعدها كيفية استخدام السمات traits لتعريف السلوك في سياق الأنواع المعممة، إذ يمكنك استخدام السمات مع الأنواع المعممة لتقييد قبول أنواع تحتوي على سلوك معين بدلًا من احتوائها لأي نوع.
</p>

<p>
	وأخيرًا، سنناقش دورات الحياة lifetimes وهي مجموعة من الأنواع المعممة التي تعطي المصرّف معلومات حول ارتباط المراجع references ببعضها بعضًا، وتسمح لنا دورات الحياة بإعطاء المصرف كمًا كافيًا من المعلومات عن القيم المُستعارة borrowed values حتى يتسنّى له التأكد من المراجع التي ستكون صالحة في أكثر من موضع مقارنةً بالمواضع التي يمكن للمصرف التحقق منها بنفسه دون مساعدتنا.
</p>

<h2>
	إزالة التكرار باستخراج الدالة
</h2>

<p>
	تسمح لنا الأنواع المعممة باستبدال أنواع محددة مع موضع مؤقت placeholder يمثل أنواع عدّة بهدف التخلص من الشيفرة البرمجية المتكررة. دعنا ننظر إلى كيفية التخلص من الشيفرات البرمجية المتكررة دون استخدام الأنواع المعممة قبل أن نتكلم عن كيفية كتابتها، وذلك عن طريق استخراج الدالة التي ستستبدل قيمًا معيّنة بموضع مؤقت يمثّل قيمًا متعددة، ثم نطبق الطريقة ذاتها لاستخراج دالة معممة. سنبدأ بالتعرف على الشيفرات البرمجية المتكررة التي يمكن أن تستخدم الأنواع المعممة عن طريق التعرف على الشيفرات البرمجية المتكررة الممكن استخراجها إلى دالة.
</p>

<p>
	دعنا نبدأ ببرنامج قصير موضّح في الشيفرة 1، إذ يعثر هذا البرنامج على أكبر رقم موجود في قائمة ما.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8664_8" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let number_list </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![</span><span class="lit">34</span><span class="pun">,</span><span class="pln"> </span><span class="lit">50</span><span class="pun">,</span><span class="pln"> </span><span class="lit">25</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">65</span><span class="pun">];</span><span class="pln">

    let mut largest </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">number_list</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> number in </span><span class="pun">&amp;</span><span class="pln">number_list </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> number </span><span class="pun">&gt;</span><span class="pln"> largest </span><span class="pun">{</span><span class="pln">
            largest </span><span class="pun">=</span><span class="pln"> number</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    println</span><span class="pun">!(</span><span class="str">"The largest number is {}"</span><span class="pun">,</span><span class="pln"> largest</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 1: العثور على أكبر رقم في قائمة من الأرقام
</p>

<p>
	نخزّن هنا قائمة من الأرقام الصحيحة في المتغير <code>number_list</code> ونعيّن مرجعًا إلى العنصر الأول في القائمة ضمن متغير يدعى <code>largest</code>، ثمّ نمرّ على العناصر الموجودة في القائمة بالترتيب ونفحص إذا كان الرقم الحالي أكبر من الرقم الذي خزّننا مرجعه في <code>largest</code>؛ فإذا كانت الإجابة نعم، نستبدل المرجع السابق بمرجع الرقم الحالي؛ وإلا -إذا كان الرقم الحالي أصغر أو تساوي من الرقم <code>largest</code>- لا نغير قيمة المتغير وننتقل إلى الرقم الذي يليه في القائمة. يجب أن يمثل <code>largest</code> مرجعًا لأكبر رقم في القائمة بعد النظر إلى كل الأرقام، وهو في هذه الحالة 100.
</p>

<p>
	تغيّرت مهمتنا الآن: علينا كتابة برنامج يفحص أكبر رقم ضمن قائمتين مختلفتين من الأرقام، ولفعل ذلك يمكننا نسخ الشيفرة البرمجية في الشيفرة 1 مرةً أخرى واستخدام المنطق ذاته في موضعين مختلفين ضمن البرنامج كما هو موضح في الشيفرة 2.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8664_10" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let number_list </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![</span><span class="lit">34</span><span class="pun">,</span><span class="pln"> </span><span class="lit">50</span><span class="pun">,</span><span class="pln"> </span><span class="lit">25</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">65</span><span class="pun">];</span><span class="pln">

    let mut largest </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">number_list</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> number in </span><span class="pun">&amp;</span><span class="pln">number_list </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> number </span><span class="pun">&gt;</span><span class="pln"> largest </span><span class="pun">{</span><span class="pln">
            largest </span><span class="pun">=</span><span class="pln"> number</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    println</span><span class="pun">!(</span><span class="str">"The largest number is {}"</span><span class="pun">,</span><span class="pln"> largest</span><span class="pun">);</span><span class="pln">

    let number_list </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![</span><span class="lit">102</span><span class="pun">,</span><span class="pln"> </span><span class="lit">34</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6000</span><span class="pun">,</span><span class="pln"> </span><span class="lit">89</span><span class="pun">,</span><span class="pln"> </span><span class="lit">54</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">43</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8</span><span class="pun">];</span><span class="pln">

    let mut largest </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">number_list</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> number in </span><span class="pun">&amp;</span><span class="pln">number_list </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> number </span><span class="pun">&gt;</span><span class="pln"> largest </span><span class="pun">{</span><span class="pln">
            largest </span><span class="pun">=</span><span class="pln"> number</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    println</span><span class="pun">!(</span><span class="str">"The largest number is {}"</span><span class="pun">,</span><span class="pln"> largest</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 2: شيفرة برمجية تجد أكبر رقم في قائمتين من الأرقام
</p>

<p>
	على الرغم من أن الشيفرة البرمجية السابقة تعمل بنجاح إلا أن نسخ الشيفرة البرمجية عملية رتيبة ومعرضة للأخطاء، علينا أيضًا أن نتذكر تعديل الشيفرة البرمجية في عدة مواضع إذا أردنا التعديل على منطق البرنامج.
</p>

<p>
	لنُنشئ حلًا مجرّدًا للتخلص من التكرار وذلك <a href="https://academy.hsoub.com/programming/rust/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-functions-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%82%D8%A7%D8%AA-comments-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%A7%D8%B3%D8%AA-rust-r1781/" rel="">بتعريف دالة</a> تعمل على أي قائمة من الأرقام الصحيحة بتمريرها مثل معامل للدالة. يجعل هذا الحل من شيفرتنا البرمجية أكثر وضوحًا ويسمح لنا بالتعبير عن مفهوم العثور على أكبر رقم ضمن قائمة ما بصورةٍ مجرّدة.
</p>

<p>
	نستخرج الشيفرة البرمجية التي تبحث عن أكبر عدد إلى دالة تدعى <code>largest</code> في الشيفرة 3، من ثم نستدعي الدالة لإيجاد أكبر عدد في القائمتين الموجودتين في الشيفرة 2، مما يمكننا من استخدام الدالة على أي قائمة تحمل عناصر من النوع <code>i32</code>.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8664_12" style=""><span class="pln">fn largest</span><span class="pun">(</span><span class="typ">list</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;[</span><span class="pln">i32</span><span class="pun">])</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">i32 </span><span class="pun">{</span><span class="pln">
    let mut largest </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="typ">list</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> item in </span><span class="typ">list</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> item </span><span class="pun">&gt;</span><span class="pln"> largest </span><span class="pun">{</span><span class="pln">
            largest </span><span class="pun">=</span><span class="pln"> item</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    largest
</span><span class="pun">}</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let number_list </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![</span><span class="lit">34</span><span class="pun">,</span><span class="pln"> </span><span class="lit">50</span><span class="pun">,</span><span class="pln"> </span><span class="lit">25</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">65</span><span class="pun">];</span><span class="pln">

    let result </span><span class="pun">=</span><span class="pln"> largest</span><span class="pun">(&amp;</span><span class="pln">number_list</span><span class="pun">);</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"The largest number is {}"</span><span class="pun">,</span><span class="pln"> result</span><span class="pun">);</span><span class="pln">

    let number_list </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![</span><span class="lit">102</span><span class="pun">,</span><span class="pln"> </span><span class="lit">34</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6000</span><span class="pun">,</span><span class="pln"> </span><span class="lit">89</span><span class="pun">,</span><span class="pln"> </span><span class="lit">54</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">43</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8</span><span class="pun">];</span><span class="pln">

    let result </span><span class="pun">=</span><span class="pln"> largest</span><span class="pun">(&amp;</span><span class="pln">number_list</span><span class="pun">);</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"The largest number is {}"</span><span class="pun">,</span><span class="pln"> result</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 3: شيفرة برمجية مجردة لإيجاد أكبر رقم ضمن قائمتين
</p>

<p>
	تقبل الدالة <code>largest</code> معاملًا يدعى <code>list</code> ويمثّل أي شريحة slice حقيقية من قيم <code>i32</code>، ونتيجةً لذلك يمكننا استدعاء الدالة وتنفيذ الشيفرة البرمجية بحسب القيم المحددة التي نمررها للدالة.
</p>

<p>
	اختصارًا لما سبق، إليك الخطوات التي اتبعناها للوصول من الشيفرة 2 إلى الشيفرة 3:
</p>

<ol>
	<li>
		التعرف على الشيفرة البرمجية المتكررة.
	</li>
	<li>
		استخراج الشيفرة البرمجية المتكررة إلى محتوى دالة وتحديد القيم التي نمررها للدالة والقيم التي تعيدها الدالة في بصمة الدالة signature.
	</li>
	<li>
		تحديث مواضع نسخ الشيفرة البرمجية لتستدعي الدالة بدلًا من ذلك.
	</li>
</ol>

<p>
	سنستخدم الخطوات ذاتها لاحقًا مع الأنواع المعممة للتقليل من الشيفرات البرمجية المكررة، إذ تسمح الأنواع المعممة للشيفرة البرمجية بالعمل على الأنواع المجردة بالطريقة ذاتها التي يتعامل فيها محتوى الدالة على قائمة مجرّدة بدلًا من قيم محددة.
</p>

<p>
	على سبيل المثال، دعنا نفترض وجود دالتين: دالة تعثر على أكبر رقم في شريحة من قيم <code>i32</code> وأخرى تعثر على أكبر قيمة في شريحة من قيم <code>char</code>، كيف يمكننا التخلص من التكرار هنا؟ هذا ما سنناقشه تاليًا.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch10-00-generics.html" rel="external nofollow">Generic Types, Traits, and Lifetimes</a> من كتاب <a href="https://doc.rust-lang.org/stable/book/title-page.html" rel="external nofollow">The Rust Programming Language</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/rust/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D9%85%D8%A9-generic-data-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1936/" rel="">كيفية استخدام أنواع البيانات المعممة Generic Data Types في لغة رست Rust</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D9%8A%D8%A7%D8%B1-%D9%85%D8%A7-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%A7%D9%83%D8%B1%D9%88-panic%E2%80%8E-%D9%88%D8%A7%D9%84%D9%86%D9%88%D8%B9-result-%D9%84%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1921/" rel="">الاختيار ما بين الماكرو panic!‎ والنوع Result للتعامل مع الأخطاء في لغة رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/java/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D9%88%D8%AA%D9%88%D8%A7%D8%A8%D8%B9-%D9%85%D8%B9%D9%85%D9%85%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1432/" rel="">كتابة أصناف وتوابع معممة في جافا</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-data-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1780/" rel="">أنواع البيانات Data Types في لغة رست Rust</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1935</guid><pubDate>Thu, 23 Mar 2023 13:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x627;&#x62E;&#x62A;&#x64A;&#x627;&#x631; &#x645;&#x627; &#x628;&#x64A;&#x646; &#x627;&#x644;&#x645;&#x627;&#x643;&#x631;&#x648; panic!&#x200E; &#x648;&#x627;&#x644;&#x646;&#x648;&#x639; Result &#x644;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x627;&#x644;&#x623;&#x62E;&#x637;&#x627;&#x621; &#x641;&#x64A; &#x644;&#x63A;&#x629; Rust</title><link>https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D9%8A%D8%A7%D8%B1-%D9%85%D8%A7-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%A7%D9%83%D8%B1%D9%88-panic%E2%80%8E-%D9%88%D8%A7%D9%84%D9%86%D9%88%D8%B9-result-%D9%84%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-rust-r1921/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_03/----panic!_--Result------Rust.jpg.e4d9965b01c03b6f01027fd49136feaf.jpg" /></p>
<p>
	كيف يمكننا الاختيار ما بين استدعاء الماكرو <code>panic!‎</code> وإعادة القيمة <code>Result</code> عند حدوث الأخطاء؟ عندما تهلع الشيفرة البرمجية (أي عند استدعاء الماكرو <code>panic!‎</code>)، فليس هناك أي طريقة لحل ذلك الخطأ، ويمكنك استدعاء <code>panic!‎</code> لأي خطأ كان.
</p>

<p>
	وسواءٌ كان خطأ يُمكن حلّه أو لا، فأنت من يتخذ القرار بجعل الخطأ هذا قابلًا للحل في شيفرتك البرمجية أم لا؛ فعندما تختار إعادة القيمة <code>Result</code>، فأنت تحاول منح الشيفرة البرمجية التي استدعت ذلك الفعل الذي تسبب بالخطأ (دالةٌ ما) بعض الخيارات <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1920/" rel="">للتعامل مع ذلك الخطأ</a> بحيث تحاول الشيفرة البرمجية حله بطريقة ملائمة لكل حالة، أو أن تحدد أن قيمة الخطأ <code>Err</code> غير قابلة للحل مما يتسبب باستدعاء الماكرو <code>panic!‎</code> وبالتالي جعل الخطأ القابل للحل خطأ غير قابل للحل. إذًا، إعادة القيمة <code>Result</code> هي خيار افتراضي جيّد عندما تعرّف دالة ما قد تفشل في بعض الأحيان.
</p>

<p>
	من المحبّذ في حالات كتابة الأمثلة والشيفرات البرمجية التجريبية كتابة شيفرة برمجية تهلع بدلًا من إعادة القيمة <code>Result</code>، دعنا ننظر إلى السبب ومن ثم نناقش الحالات التي قد لا يستطيع فيها المصرّف إخبارنا بإمكانية حدوث خطأ ما، إلا أنك تستطيع فعل ذلك. سنختتم أيضًا هذا المقال بتوجيهات عامة تساعدك في تحديد الحالات التي يكون فيها جعل الشيفرة البرمجية تهلع بصورةٍ أفضل.
</p>

<h2>
	أمثلة وشيفرات برمجية تجريبية واختبارات
</h2>

<p>
	استخدام شيفرة برمجية تتعامل مع الأخطاء بصورةٍ جيدة عندما تكتب مثالًا ما لتوضيح مفهوم معين يجعل من المثال أقل وضوحًا، ونستدعي عادةً في الأمثلة تابعًا مثل <code>unwrap</code> يمكن أن يهلع مثل موضع مؤقت placeholder من أجل الطريقة التي تريد التعامل مع الأخطاء في تطبيقك، والتي قد تختلف بناءً على ما تفعله بقية الشيفرة البرمجية.
</p>

<p>
	التابعان <code>unwrap</code> و <code>expect</code> مفيدان جدًا أيضًا عند كتابة الشيفرات البرمجية التجريبية قبل أن تتخذ قرار بخصوص التعامل مع الأخطاء، إذ يساعدك التابعان بترك علامات واضحة في شيفرتك البرمجية انتظارًا للوقت الذي تريد فيه جعل برنامجك أكثر متانةً بالتعامل مع الأخطاء.
</p>

<p>
	إذا فشل استدعاء تابع ما ضمن اختبار فمن الأفضل جعل كل الاختبار يفشل حتى لو كان التابع ذلك غير مضمّنٍ في الاختبار الأساسي، ولأن الماكرو <code>panic!‎</code> هو بمثابة إشارة إلى أن الاختبار سيفشل، فإن استدعاء <code>unwrap</code> أو <code>expect</code> هو ما يجب حدوثه.
</p>

<h2>
	الحالات التي تعرف فيها معلومات أكثر من المصرف
</h2>

<p>
	من الملائم أيضًا استدعاء <code>unwrap</code> أو <code>expect</code> عند تواجد منطق ما يضمن أن <code>Result</code> ستحتوي على قيمة <code>Ok</code> إلا أن هذا المنطق لا يمكن فهمه من قبل المصرّف، إذ ستتواجد قيمة <code>Result</code> بحاجة للتعامل معها وفحصها، فأي عملية تستدعيها قد تفشل عمومًا على الرغم من أن الأمر مستحيل منطقيًا في هذه الحالة. من المقبول استدعاء <code>unwrap</code> إذا استطعت أن تتأكد بفحص الشيفرة البرمجية يدويًا أنها لن تحتوي على متغاير <code>Err</code> أبدًا، والأفضل في هذه الحالة أن توثّق السبب الذي تعتقد أنك لن تحصل فيه على متغاير <code>Err</code> في نص <code>expect</code>. إليك مثالًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9368_6" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    use std</span><span class="pun">::</span><span class="pln">net</span><span class="pun">::</span><span class="typ">IpAddr</span><span class="pun">;</span><span class="pln">

    let home</span><span class="pun">:</span><span class="pln"> </span><span class="typ">IpAddr</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"127.0.0.1"</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">parse</span><span class="pun">()</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">expect</span><span class="pun">(</span><span class="str">"Hardcoded IP address should be valid"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نُنشئ هنا نسخةً من <code>IpAddr</code> بالمرور على السلسلة النصية المكتوبة في الشيفرة البرمجية، ويمكن ملاحظة أن "127.0.0.1" هو عنوان IP صالح وبالتالي من المقبول استخدام <code>expect</code> هنا، إلا أن وجود سلسلة نصية صالحة مكتوبة في الشيفرة البرمجية لا يغير من النوع المعاد للتابع <code>parse</code>، إذ أننا ما زلنا نحصل على قيمة <code>Result</code> وسيجبرنا المصرف على التعامل مع <code>Result</code> لأنه يفترض أن وجود المتغاير <code>Err</code> بداخل <code>Result</code> أمر ممكن الحدوث وذلك لأن المصرف ليس ذكي بالقدر الكافي ليرى أن السلسلة النصية تمثل عنوان IP صالح دومًا.
</p>

<p>
	يوجد احتمال بوجود المتغاير <code>Err</code> إذا أتى عنوان IP مثل دخل من المستخدم بدلًا من كتابته مباشرةً في الشيفرة البرمجية وعندها علينا أن نتعامل مع <code>Result</code> بطريقة شاملة بدلًا من الطريقة الحالية. ذكر الافتراض أن عنوان IP هذا مكتوب في الشيفرة البرمجية وبالتالي سيُطلب منّا تغيير <code>expect</code> للحصول على شيفرة برمجية تتعامل مع الأخطاء بصورةٍ أفضل في المستقبل، وبالتالي فنحن بحاجة للحصول على عنوان IP من مصادر أخرى.
</p>

<h2>
	توجيهات للتعامل مع الأخطاء
</h2>

<p>
	يُنصح بجعل الشيفرة البرمجية تهلع عندما يمكن أن يؤدي الخطأ إلى حالة سيئة bad state للشيفرة البرمجية، ونقصد هنا بالحالة السيئة الحالة التي يتغير فيها افتراض assumption، أو ضمان guarantee، أو عقد contract، أو ثابت invariant، مثل الحصول على قيم غير صحيحة أو متناقضة أو مفقودة، إضافةً إلى واحدة أو أكثر من الحالات التالية:
</p>

<ul>
	<li>
		الحالة السيئة هي حالة غير متوقعة لا تحدث عادةً، مثل إدخال المستخدم بياناته بتنسيق خاطئ.
	</li>
	<li>
		يجب أن تعتمد شيفرتك البرمجية بعد حدوث هذه الحالة على فحص المشكلة بعد كل خطوة بدلًا من اتخاذ معطيات الحالة السيئة هذه.
	</li>
	<li>
		لا توجد طريقة مثالية لتمثيل هذه <a href="https://academy.hsoub.com/programming/rust/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-data-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1780/" rel="">البيانات</a> في الأنواع التي تستخدمها، وسننظر إلى مثال على ذلك في المقالات القادمة.
	</li>
</ul>

<p>
	إذا استدعى أحدهم شيفرتك البرمجية ومرّر لها القيم التي تؤدي إلى حالة سيئة، فمن الأفضل إعادة الخطأ إذا استطعت، حتى يقرّر مستخدم المكتبة الإجراء الذي يريد اتخاذه في هذه الحالة، إلا أن الاستمرار بتنفيذ الشيفرة البرمجية في بعض الأحيان قد يكون غير آمن أو ضار، وفي هذه الحالة يكون استدعاء الماكرو <code>panic!‎</code> أفضل خيار لتنبيه المستخدم الذي يستخدم المكتبة بالخطأ الموجود في شيفرته البرمجية وكيف يستطيع إصلاحه خلال عملية التطوير. استخدام <code>panic!‎</code> أيضًا مناسب في حال استدعاء الشيفرة البرمجية الخارجية التي لا تستطيع التحكم بها وإعادة حالة غير صالحة لا يمكنك إصلاحها.
</p>

<p>
	من الملائم عند الحالات التي تتوقع فيها حدوث فشل أن تُعيد القيمة <code>Result</code> بدلًا من استدعاء <code>panic!‎</code>، مثل تمرير بيانات مشوّهة وتحليلها، أو <a href="https://academy.hsoub.com/programming/general/%d9%85%d8%af%d8%ae%d9%84-%d8%a5%d9%84%d9%89-http-r73/" rel="">طلب HTTP</a> يُعيد حالة تُشير إلى وصول حدٍ معدّل ما rate limit، وفي هذه الحالة تُشير إعادة القيمة <code>Result</code> إلى أن الخطأ الذي حصل هو خطأ متوقع حدوثه عند استدعاء الشيفرة البرمجية، التي ستحدّد كيفية التعامل مع الخطأ.
</p>

<p>
	يجب أن تتأكد شيفرتك البرمجية من القيم الصالحة أولًا وتهلع إذا لم تكن القيم صالحةً عندما تُجري الشيفرة عمليةً يمكن أن تضع المستخدم في خطرٍ ما عند استدعائها باستخدام قيم غير صالحة، وذلك لأسباب تتعلق بالأمان، إذ أن محاولة إجراء عمليةٍ على بيانات غير صالحة قد تعرّض شيفرتك البرمجية لثغرات أمنية، وهذا هو السبب الرئيس لاستدعاء المكتبة القياسية للماكرو <code>panic!‎</code> إذا حاولت الوصول إلى عنصر يقع خارج حدود الذاكرة out-of-bound memory access، مثل محاولة الوصول إلى حيز ذاكرة لا ينتمي إلى <a href="https://academy.hsoub.com/programming/general/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-data-structures/" rel="">هيكل البيانات</a> الحالي، وهي مشكلة أمنية شائعة.
</p>

<p>
	تحتوي <a href="https://academy.hsoub.com/programming/rust/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-functions-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%82%D8%A7%D8%AA-comments-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%A7%D8%B3%D8%AA-rust-r1781/" rel="">الدوال</a> عادةً على ما يُدعى بالعقود contracts، إذ أن تصرُّف الدوال مضمونٌ فقط في حال كان الدخل يوافق بعض المتطلبات، فإذا أُخلّ بالعقد، سنحصل على حالة هلع وهذا الأمر منطقي، لأن الإخلال بالعقد يعني أن هناك خطأ من طرف مستدعي الشيفرة البرمجية، وهو نوع من الأخطاء لا تجد للشيفرة البرمجية المستدعية ضرورةً لمعالجته، وفي الحقيقة لا توجد هناك أي طريقة منطقية حتى تتعافى الشيفرة البرمجية المُستدعية، إذ يقع إصلاح الخطأ على عاتق المبرمج الذي استدعى هذه الشيفرة البرمجية. تسبب عقود الدوال عند خرقها حالة هلع، وتُشرح العقود عادةً في توثيق <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">الواجهة البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> الخاص بالدالة.
</p>

<p>
	الحصول على الكثير من الأخطاء في جميع الدوال أمرٌ رتيب ومزعج، ولحس الحظ يمكننا استخدام نظام أنواع <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-%D8%A7%D9%84%D8%A8%D8%AF%D8%A7%D9%8A%D8%A7%D8%AA-r1764/" rel="">لغة رست</a> (وبالتالي التحقق من الأنواع من المصرّف) لإجراء العديد من الفحوصات نيابةً عنك.
</p>

<p>
	إذا كانت الدالة تقبل نوعًا محددًا للمعامل، فيمكنك متابعة منطق شيفرتك البرمجية على أساس هذا النوع وأنت مطمئن، لأن المصرف سيتأكد من استخدام النوع الصحيح دومًا، فعلى سبيل المثال سيتوقع برنامجك قيمةً ما بدلًا من عدم وجود قيمة إذا كان لديك نوع معيّن بدلًا من <code>Option</code>، وعندها لن يتوجب على شيفرتك البرمجية أن تتعامل مع متغاير <code>Some</code> و <code>None</code> على حدى، بل ستتحقق من حالة واحدة فقط وهو وجود قيمة، وبالتالي لن تستطيع تصريف أي شيفرة برمجية لا تمرر قيمة إلى الدالة ولن يتوجب عندها التحقق من أن الدالة تحصل على قيمة خلال وقت التشغيل.
</p>

<p>
	وفي مثال على ما سبق، يمكن الإشارة إلى استخدام نوع العدد الصحيح عديم الإشارة unsigned integer مثل <code>u32</code>، وهذا يضمن لك أن تكون قيمة المعامل موجبةً دومًا.
</p>

<h2>
	إنشاء أنواع مخصصة بهدف التحقق
</h2>

<p>
	دعنا نتطرق إلى فكرة استخدام <a href="https://academy.hsoub.com/programming/rust/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-data-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1780/" rel="">نظام أنواع رست</a> للتأكد من أن القيمة صالحة، وذلك بإنشاء نوع مخصص للتحقق.
</p>

<p>
	تذكّر <a href="https://academy.hsoub.com/programming/rust/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%84%D8%B9%D8%A8%D8%A9-%D8%AA%D8%AE%D9%85%D9%8A%D9%86-%D8%A7%D9%84%D8%A3%D8%B1%D9%82%D8%A7%D9%85-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1765/" rel="">لعبة تخمين الأرقام</a> التي أنشأناها سابقًا، إذ طلبنا من المستخدم حينها تخمين رقم يقع بين 1 و100، ولم نتحقق حينها من أن تخمين المستخدم كان ضمن هذا المجال قبل أن نتحقق من أن التخمين يطابق الرقم السري (الإجابة الصحيحة)، إذ أننا تأكدنا وقتها من أن القيمة موجبة فقط، وفي هذه الحالة فإن البرنامج لن يكون خاطئًا، لأن طباعة "تخمينك كبير" أو "تخمينك صغير" سيعمل كم هو مطلوب، إلا أنه من الأفضل تحسين البرنامج بحيث يرشد المستخدم نحو تخمين صحيح في حال كان تخمينه ضمن المجال ويختلف سلوك البرنامج في حال كان التخمين خارج المجال المطلوب، كما هو الحال عندما يكتب المستخدم أحرفًا بدل أرقام.
</p>

<p>
	نستطيع تحقيق ذلك عن طريق النظر إلى تخمين المستخدم على أنه نوع <code>i32</code> بدلًا من <code>u32</code> للسماح بقيم سالبة ومن ثم التأكد من أن الرقم متواجد ضمن المجال كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9368_8" style=""><span class="pln">use rand</span><span class="pun">::</span><span class="typ">Rng</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">cmp</span><span class="pun">::</span><span class="typ">Ordering</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">io</span><span class="pun">;</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"Guess the number!"</span><span class="pun">);</span><span class="pln">

    let secret_number </span><span class="pun">=</span><span class="pln"> rand</span><span class="pun">::</span><span class="pln">thread_rng</span><span class="pun">().</span><span class="pln">gen_range</span><span class="pun">(</span><span class="lit">1.</span><span class="pun">.=</span><span class="lit">100</span><span class="pun">);</span><span class="pln">

    loop </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// --snip--</span><span class="pln">

        println</span><span class="pun">!(</span><span class="str">"Please input your guess."</span><span class="pun">);</span><span class="pln">

        let mut guess </span><span class="pun">=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">();</span><span class="pln">

        io</span><span class="pun">::</span><span class="pln">stdin</span><span class="pun">()</span><span class="pln">
            </span><span class="pun">.</span><span class="pln">read_line</span><span class="pun">(&amp;</span><span class="pln">mut guess</span><span class="pun">)</span><span class="pln">
            </span><span class="pun">.</span><span class="pln">expect</span><span class="pun">(</span><span class="str">"Failed to read line"</span><span class="pun">);</span><span class="pln">

        let guess</span><span class="pun">:</span><span class="pln"> i32 </span><span class="pun">=</span><span class="pln"> match guess</span><span class="pun">.</span><span class="pln">trim</span><span class="pun">().</span><span class="pln">parse</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">Ok</span><span class="pun">(</span><span class="pln">num</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> num</span><span class="pun">,</span><span class="pln">
            </span><span class="typ">Err</span><span class="pun">(</span><span class="pln">_</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">continue</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">};</span><span class="pln">

        </span><span class="kwd">if</span><span class="pln"> guess </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> guess </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">100</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            println</span><span class="pun">!(</span><span class="str">"The secret number will be between 1 and 100."</span><span class="pun">);</span><span class="pln">
            </span><span class="kwd">continue</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        match guess</span><span class="pun">.</span><span class="pln">cmp</span><span class="pun">(&amp;</span><span class="pln">secret_number</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="com">// --snip--</span><span class="pln">
            </span><span class="typ">Ordering</span><span class="pun">::</span><span class="typ">Less</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"Too small!"</span><span class="pun">),</span><span class="pln">
            </span><span class="typ">Ordering</span><span class="pun">::</span><span class="typ">Greater</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"Too big!"</span><span class="pun">),</span><span class="pln">
            </span><span class="typ">Ordering</span><span class="pun">::</span><span class="typ">Equal</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                println</span><span class="pun">!(</span><span class="str">"You win!"</span><span class="pun">);</span><span class="pln">
                </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يتحقق تعبير <code>if</code> فيما إذا كانت القيمة خارج المجال ويخبر المستخدم بالمشكلة، ثم يستدعي <code>continue</code> للبدء بالتكرار التالي من الحلقة لسؤال المستخدم عن التخمين مرةً أخرى. يمكننا الاستمرار بتنفيذ المقارنات بعد تعبير <code>if</code> وذلك بين <code>guess</code> والرقم السري بمعرفة أن <code>guess</code> الآن هي ضمن المجال من 1 إلى 100، إلا أن الحل السابق ليس بالحل المثالي، فوجود عملية تحقق مثل هذه في برنامج يعمل فقط على القيم التي تقع ما بين 1 و100 عملية رتيبة، إذ علينا تكرار عملية التحقق داخل كل دالة في هذا البرنامج، كما قد يؤثر ذلك على أداء البرنامج.
</p>

<p>
	يمكننا إنشاء نوع جديد عوضًا عمّا سبق وأن نضع عملية التحقق في دالة، ومن ثم يمكننا إنشاء نسخة عن النوع بدلًا من تكرار عملية التحقق في كل مكان، وبهذه الطريقة يصبح استخدام الدوال للنوع الجديد أكثر أمانًا في بصمتها signature، إذ أنها ستستخدم القيم المسموحة في البرنامج فقط. توضح الشيفرة 13 طريقةً لتعريف النوع <code>Guess</code> الذي يُنشئ نسخةً من <code>Guess</code> إذا استقبلت الدالة <code>new</code> قيمةً ما بين 1 و100.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9368_10" style=""><span class="com">#![allow(unused)]</span><span class="pln">
fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
pub </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Guess</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    value</span><span class="pun">:</span><span class="pln"> i32</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

impl </span><span class="typ">Guess</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pub fn </span><span class="kwd">new</span><span class="pun">(</span><span class="pln">value</span><span class="pun">:</span><span class="pln"> i32</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Guess</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> value </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> value </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">100</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            panic</span><span class="pun">!(</span><span class="str">"Guess value must be between 1 and 100, got {}."</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="typ">Guess</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> value </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    pub fn value</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> i32 </span><span class="pun">{</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">value
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 13: نوع <code>Guess</code> سيستمر فقط في حال كانت القيم بين 1 و100
</p>

<p>
	نعرّف <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%87%D9%8A%D8%A7%D9%83%D9%84-structs-%D9%84%D8%AA%D9%86%D8%B8%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1849/" rel="">هيكلًا</a> أولًا باسم <code>Guess</code> يحتوي على الحقل field <code>value</code> الذي يخزن بداخله قيمة من النوع <code>i32</code>، وهو الحقل الذي سنخزّن فيه تخمين المستخدم. نطبّق بعدها دالةً مرتبطة بالهيكل‏ <code>Guess</code> تدعى <code>new</code>، إذ تُنشئ هذه الدالة نسخةً من قيم <code>Guess</code> وهي معرفة بحيث تتلقى معاملًا واحدًا يُدعى <code>value</code> نوعه <code>i32</code> وأن تعيد هيكلًا من النوع <code>Guess</code>.
</p>

<p>
	تفحص الشيفرة البرمجية داخل الدالة <code>new</code> القيمة <code>value</code> لتتأكد من أنها تقع ما بين 1 و100، وإذا لم تحقق <code>value</code> هذا الشرط، نستدعي الماكرو <code>panic!‎</code> الذي سينبّه المبرمج الذي يكتب الشيفرة البرمجية المُستدعية للدالة أن شيفرته البرمجية تحتوي على خطأ يجب إصلاحه لأن إنشاء <code>Guess</code> بقيمة <code>value</code> خارج النطاق المحدد سيخرق الاتفاق الذي تعتمد عليه الدالة <code>Guess::new</code>.
</p>

<p>
	يجب أن تُناقش الحالات التي قد تهلع فيها الدالة <code>Guess::new</code> في توثيق الواجهة البرمجية العلني الخاص بالدالة، وسنغطّي اصطلاحات التوثيق التي تُشير فيها لاحتمال حصول حالة هلع <code>panic!‎</code> في توثيق الواجهة البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> لاحقًا.
</p>

<p>
	نُنشئ هيكل <code>Guess</code> جديد بحقل <code>value</code> قيمته مساوية إلى المعامل <code>value</code> ونُعيد <code>Guess</code> إذا لم تتخطى <code>value</code> الاختبار، <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D9%88%D8%A7%D8%A8%D8%B9-methods-%D8%B6%D9%85%D9%86-%D8%A7%D9%84%D9%87%D9%8A%D8%A7%D9%83%D9%84-structs-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1850/" rel="">ثم نطبّق تابعًا</a> يدعى <code>value</code> يستعير <code>self</code> ولا يمتلك أي معاملات أخرى، ويعيد قيمةً من نوع <code>i32</code>، ويُدعى هذا النوع من التوابع بالجالب getter لأن الهدف منه يكمن في الحصول على بيانات من حقوله وإعادتها.
</p>

<p>
	هذا التابع العام public method مهم لأن الحقل <code>value</code> في الهيكل <code>Guess</code> هو هيكل خاص private، ومن المهم لحقل <code>value</code> أن يكون خاصًا بحيث لا يُسمح للشيفرة البرمجية التي تستخدم الهيكل <code>Guess</code> بأن تضبط قيمة <code>value</code> مباشرةً؛ إذ يجب على الشيفرة البرمجية التي تقع خارج الوحدة module أن تستخدم الدالة <code>Guess::new</code> لإنشاء نسخة من <code>Guess</code> للتأكد من أنه لا توجد أي طريقة أن يحتوي الهيكل <code>Guess</code> على قيمة حقل <code>value</code> دون فحصها ومطابقتها للشروط ضمن الدالة <code>Guess::new</code>.
</p>

<p>
	يمكن لدالة تحتوي معاملًا أو تعيد أرقام ضمن المجال 1 إلى 100 التصريح ضمن بصمتها بأنها أنها تأخذ أو تعيد <code>Guess</code> بدلًا عن <code>i32</code> وعندها لن تحتاج الدالة إلى إجراء أي عمليات تحقّق إضافية ضمنها.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch09-00-error-handling.html" rel="external nofollow">Error Handling</a> من كتاب <a href="https://doc.rust-lang.org/stable/book/title-page.html" rel="external nofollow">The Rust Programming Language</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/rust/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D9%85%D8%A9-generic-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-rust-r1935/" rel="">مقدمة إلى مفهوم الأنواع المعممة Generic Types في لغة Rust</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1920/" rel="">الأخطاء والتعامل معها في لغة رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A8%D8%B3%D9%8A%D8%B1-%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%B1%D8%A7%D8%B3%D8%AA-rust-r1785/" rel="">التحكم بسير تنفيذ برامج راست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D8%A8%D8%AA%D8%B1%D9%85%D9%8A%D8%B2-utf-8-%D8%AF%D8%A7%D8%AE%D9%84-%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1876/" rel="">تخزين النصوص بترميز UTF-8 داخل السلاسل النصية في لغة رست Rust</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1921</guid><pubDate>Thu, 16 Mar 2023 15:09:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x623;&#x62E;&#x637;&#x627;&#x621; &#x648;&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639;&#x647;&#x627; &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A; Rust</title><link>https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%D9%87%D8%A7-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1920/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_03/------Rust.jpg.2cf3a19b0b96f644944a59d4c2d192b3.jpg" /></p>
<p>
	لا مهرب من الأخطاء في دورة تطوير البرمجيات، لذا توفّر رست عددًا من المزايا للتعامل مع الحالات التي يحدث فيها شيء خاطئ، وتطلب رست منك في العديد من الحالات معرفتك باحتمالية حدوث الخطأ واتخاذ فعل ما قبل أن تُصرَّف compile الشيفرة البرمجية، ويجعل ذلك من برنامجك أكثر قوة بالتأكد من أنك ستكتشف الخطأ وستتعامل معه على نحوٍ مناسب قبل إطلاق شيفرتك البرمجية إلى مرحلة الإنتاج.
</p>

<p>
	تصنِّف رست الأخطاء ضمن مجموعتين:
</p>

<ul>
	<li>
		<strong>الأخطاء القابلة للحل recoverable errors</strong>
	</li>
	<li>
		<strong>الأخطاء غير القابلة للحل unrecoverable errors</strong>
	</li>
</ul>

<p>
	بالنسبة للأخطاء القابلة للحل، فهي أخطاء الهدف منها إعلام المستخدم بالمشكلة وإعادة محاولة العملية ذاتها مثل خطأ "لم يُعثَر على الملف file not found"، بينما تدل الأخطاء غير القابلة للحل دائمًا على أخطاء في الشيفرة البرمجية مثل محاولة الوصول إلى موقع يقع خارج نهاية مصفوفة وهذا يعني أننا نريد إيقاف تنفيذ البرنامج مباشرةً.
</p>

<p>
	لا تميّز معظم <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغات البرمجة</a> بين النوعين السابقين وتتعامل معهما بنقس الطريقة باستخدام الاستثناءات exceptions، إلا أن رست لا تحتوي على الاستثناءات بل تحتوي على النوع <code>Result&lt;T, E&gt;‎</code> للأخطاء القابلة للحل <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D9%8A%D8%A7%D8%B1-%D9%85%D8%A7-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%A7%D9%83%D8%B1%D9%88-panic%E2%80%8E-%D9%88%D8%A7%D9%84%D9%86%D9%88%D8%B9-result-%D9%84%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1921/" rel="">والماكرو panic!‎</a> الذي يوقف تنفيذ البرنامج عندما يصادف خطئًا غير قابل للحل، وسنغطّي في هذه المقال كلًا من استدعاء الماكرو <code>panic!‎</code> والحصول على قيم النوع <code>Result&lt;T, E&gt;‎</code>.
</p>

<p>
	قبل التعرف على أنواع الأخطاء وكيفية التعامل معها في لغة رست Rust، ندعوك للتعرف على الأخطاء البرمجية عامةً أولًا والتعرف على كيفية التعامل معها:
</p>

<p>
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="603" id="ips_uid_9415_6" src="https://academy.hsoub.com/applications/core/interface/index.html" title="كيفية التعامل مع الأخطاء البرمجية" width="1072" data-embed-src="https://www.youtube.com/embed/Pgje6nWuDkg"></iframe>
</p>

<h2>
	الأخطاء غير القابلة للحل باستخدام الماكرو panic!‎
</h2>

<p>
	قد تحدث بعض الأخطاء من حين إلى الآخر في شيفرتك البرمجية، ولا يوجد أي شيء تستطيع فعله لتمنع ظهورها، وفي هذه الحالة توفر لك رست الماكرو <code>panic!‎</code>.
</p>

<p>
	هناك طريقتان لبدء حالة هلع panic، هما:
</p>

<ul>
	<li>
		فعل شيء يتسبب بهلع الشيفرة البرمجية، مثل محاولة الوصول إلى مكان خارج نطاق مصفوفة.
	</li>
	<li>
		أو استدعاء الماكرو <code>panic!‎</code> مباشرةً.
	</li>
</ul>

<p>
	تتسبب الحالتين السابقتين بحالة هلع لبرنامجنا وتطبع حالات الهلع هذه افتراضيًا رسالة فشل ومن ثم تفرّغ محتويات المكدس stack وتغادر البرنامج. يمكنك عرض محتويات استدعاء المكدس عند حدوث حالة الهلع في <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-%D8%A7%D9%84%D8%A8%D8%AF%D8%A7%D9%8A%D8%A7%D8%AA-r1764/" rel="">لغة رست</a> باستخدام متغير بيئة environment variable وذلك حتى تصبح مهمة تتبع مصدر حالة الهلع أسهل.
</p>

<h3>
	كيفية الاستجابة إلى حالة هلع panic
</h3>

<p>
	يبدأ البرنامج باستعادة الحالة الأولية unwinding افتراضيًا عند حدوث حالة هلع، وهذا يعني أن رست تسترجع القيم الموجودة في المكدس وتفرغها، وتتضمن هذه العملية الكثير من العمل، لذلك تسمح لك رست باختيار الحل الثاني ألا وهو الخروج من البرنامج مباشرةً مما يُنهي تنفيذ البرنامج دون تفريغ المكدس.
</p>

<p>
	عندها، تقع مسؤولية تحرير <a href="https://academy.hsoub.com/programming/rust/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-data-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1780/" rel="">البيانات</a> المستخدمة من البرنامج على عاتق <a href="https://academy.hsoub.com/files/24-%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86/" rel="">نظام التشغيل</a>. إذا أردت الحصول على ملف تنفيذي في مشروعك بحجم صغير قدر الإمكان فعليك عندها التحويل من استعادة الحالة الأولية إلى الخروج من البرنامج فور حدوث حالة هلع بكتابة <code>panic = 'abort'‎</code> في قسم <code>[profile]</code> المناسب في ملف Cargo.toml. على سبيل المثال، إذا أردت الخروج من البرنامج فور حدوث حالة هلع في نمط الإطلاق release mode، فعليك بإضافة التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6562_6" style=""><span class="pun">[</span><span class="pln">profile</span><span class="pun">.</span><span class="pln">release</span><span class="pun">]</span><span class="pln">
panic </span><span class="pun">=</span><span class="pln"> </span><span class="str">'abort'</span></pre>

<p>
	دعنا نجرّب استدعاء الماكرو <code>panic!‎</code> في برنامج بسيط:
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6562_8" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    panic</span><span class="pun">!(</span><span class="str">"crash and burn"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عند تشغيل البرنامج ستحصل على خرج مشابه لما يلي:
</p>

<pre class="ipsCode" id="ips_uid_6562_10">$ cargo run
   Compiling panic v0.1.0 (file:///projects/panic)
    Finished dev [unoptimized + debuginfo] target(s) in 0.25s
     Running `target/debug/panic`
thread 'main' panicked at 'crash and burn', src/main.rs:2:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
</pre>

<p>
	يتسبب استدعاء الماكرو <code>panic!‎</code> برسالة الخطأ السابقة والموضحة في السطرين الأخيرين. يوضح السطر الأول رسالة الهلع ومكان حدوثه في شيفرتنا البرمجية، إذ يدل "src/main.rs:2:5" على أن حالة الهلع حدثت في السطر الثاني في المحرف الخامس ضمن الملف src/main.rs، ويكون السطر المشار إليه هو سطر ضمن شيفرتنا البرمجية التي كتبناها، وإذا ذهبنا إلى المكان المُحدّد فسنجد استدعاء الماكرو <code>panic!‎</code>.
</p>

<p>
	قد يكون استدعاء الماكرو في حالات أخرى ضمن شيفرة برمجية أخرى تستدعيها شيفرتنا البرمجية وحينها سيكون اسم الملف ورقم السطر في رسالة الخطأ عائدين لشيفرة برمجية خاصة مكتوبة من قبل شخص آخر غيرنا وليس السطر الخاص بشيفرتنا البرمجية الذي أدى لاستدعاء <code>panic!‎</code>. يمكننا تتبع مسار backtrace الدالة التي استدعت <code>panic!‎</code> لمعرفة الجزء الذي تسبب بالمشكلة ضمن شيفرتنا البرمجية، وسنناقش تتبع مسار الخطأ بالتفصيل تاليًا.
</p>

<h3>
	تتبع مسار panic!‎
</h3>

<p>
	دعنا ننظر إلى مثال آخر لرؤية ما الذي يحدث عندما يُستدعى الماكرو <code>panic!‎</code> من مكتبة بسبب خطأ في شيفرتنا البرمجية، وذلك بدلًا من استدعائه من ضمن شيفرتنا البرمجية مباشرةً. توضح الشيفرة 1 محاولة الوصول إلى دليل في شعاع خارج نطاق الأدلة الصالحة.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6562_12" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let v </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">];</span><span class="pln">

    v</span><span class="pun">[</span><span class="lit">99</span><span class="pun">];</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[شيفرة 1: محاولة الوصول إلى عنصر يقع خارج نهاية شعاع مما سيتسبب باستدعاء الماكرو panic!‎]
</p>

<p>
	نحاول هنا الوصول إلى العنصر المئة في الشعاع (وهو العنصر ذو الدليل 99 لأن عدّ الأدلة يبدأ من الصفر)، إلا أن الشعاع يحتوي على ثلاثة عناصر فقط، وفي هذه الحالة تهلع رست؛ إذ من المفترض أن استخدام <code>[]</code> سيعيد قيمة عنصر إلا أن تمرير دليل غير صالح يتسبب بهلع رست لأنها لا تعلم القيمة التي يجب أن تُعيدها بصورةٍ صحيحة.
</p>

<p>
	تتسبب هذه المحاولة في لغة سي C بسلوك غير معرف undefined behaviour، إذ من الممكن أن تحصل على قيمة عشوائية في مكان الذاكرة تلك على الرغم من أن حيز الذاكرة ذلك لا ينتمي إلى هيكل البيانات ويُدعى هذا الأمر بتجاوز المخزن المؤقت buffer overread، ويمكن أن يتسبب بخطورات أمنية إذا استطاع المهاجم التلاعب بالدليل بطريقة تمكّنه من قراءة معلومات لا يُفترض له أن يقرأها بحيث تكون مخزّنة بعد هيكل البيانات.
</p>

<p>
	لحماية برنامجك من هذا النوع من الثغرات، توقف رست تنفيذ البرنامج وترفض المتابعة إذا حاولت قراءة عنصر موجود في دليل خارج النطاق، دعنا نجرّب ذلك ونرى ما الذي يحدث:
</p>

<pre class="ipsCode">$ cargo run
   Compiling panic v0.1.0 (file:///projects/panic)
    Finished dev [unoptimized + debuginfo] target(s) in 0.27s
     Running `target/debug/panic`
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', src/main.rs:4:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
</pre>

<p>
	تُشير رسالة الخطأ إلى السطر 4 ضمن ملف "main.rs" وذلك هو السطر الذي نحاول عنده الوصول إلى الدليل 99.
</p>

<p>
	تُخبرنا الملاحظة التالية أنه يمكننا ضبط متغير البيئة <code>RUST_BACKTRACE</code> للحصول على مسار تتبع الخطأ ومعرفة سبب حدوثه، إذ يمثّل مسار تتبع الخطأ لائحةً من جميع <a href="https://academy.hsoub.com/programming/rust/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-functions-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%82%D8%A7%D8%AA-comments-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%A7%D8%B3%D8%AA-rust-r1781/" rel="">الدوال</a> التي استُدعيت إلى نقطة حدوث حالة الهلع، ويعمل في رست على نحوٍ مماثل <a href="https://academy.hsoub.com/programming/general/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">للغات البرمجة</a> الأخرى كما يلي: المفتاح في قراءة مسار تتبع الخطأ هو البدء من البداية إلى نقطة وصولك للملفات التي كتبتها إذ أن الملفات التي كتبتها ستكون نقطة ظهور المشكلة، والسطور التي تقع قبل تلك النقطة هي السطور التي استدعتها شيفرتك البرمجية والسطور التي تلي تلك النقطة هي السطور التي استدعَت شيفرتك البرمجية، وقد تتضمن كل من هذه السطور شيفرة برمجية خاصة برست أو شيفرة برمجية خاصة بالمكتبة القياسية أو وحدات مُصرّفة crates تستخدمها.
</p>

<p>
	دعنا نجرب الحصول على مسار تتبع الخطأ بضبط متغير البيئة <code>RUST_BACKTRACE</code> إلى أي قيمة عدا 0، وسيكون خرج الشيفرة 2 التالي مشابهًا لما ستحصل عليه عندها.
</p>

<pre class="ipsCode">$ RUST_BACKTRACE=1 cargo run
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', src/main.rs:4:5
stack backtrace:
   0: rust_begin_unwind
             at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panicking.rs:584:5
   1: core::panicking::panic_fmt
             at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/core/src/panicking.rs:142:14
   2: core::panicking::panic_bounds_check
             at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/core/src/panicking.rs:84:5
   3: &lt;usize as core::slice::index::SliceIndex&lt;[T]&gt;&gt;::index
             at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/core/src/slice/index.rs:242:10
   4: core::slice::index::&lt;impl core::ops::index::Index&lt;I&gt; for [T]&gt;::index
             at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/core/src/slice/index.rs:18:9
   5: &lt;alloc::vec::Vec&lt;T,A&gt; as core::ops::index::Index&lt;I&gt;&gt;::index
             at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/alloc/src/vec/mod.rs:2591:9
   6: panic::main
             at ./src/main.rs:4:5
   7: core::ops::function::FnOnce::call_once
             at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/core/src/ops/function.rs:248:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
</pre>

<p style="text-align: center;">
	[الشيفرة 2: مسار تتبع الخطأ المولّد من استدعاء الماكرو panic!‎ والذي يُعرض عند ضبط متغير البيئة RUST_BACKTRACE]
</p>

<p>
	هناك الكثبهسير من المعلومات في الخرج، وقد يكون الخرج الذي تراه أمامك مختلفًا عمّا ستحصل عليه بحسب نظام تشغيلك وإصدار رست. علينا تمكين رموز تنقيح الأخطاء debug symbols للحصول على مسار تتبع الأخطاء بالتفاصيل هذه، إذ تكون رموز تنقيح الأخطاء مفعّلة افتراضيًا باستخدام <code>cargo build</code> أو <code>cargo run</code> دون استخدام الراية <code>‎--release</code> كما هو الحال هنا.
</p>

<p>
	يدل السطر 6 في الشيفرة 2 إلى أن مسار تتبع الأخطاء يشير إلى السطر المسبب للمشكلة في مشروعنا ألا وهو السطر 4 في الملف src/main.rs، وإن لم نرد لبرنامجنا أن يهلع فعلينا البدء بالنظر إلى ذلك المكان المحدد في السطر الأول الذي يذكر الملف الذي كتبناه وهو الشيفرة 1 الذي يحتوي على شيفرة برمجية تتسبب بالهلع عمدًا، وتكمن طريقة حل حالة الهلع هذه في عدم محاولة الوصول إلى عنصر يقع خارج نطاق أدلة الشعاع. عليك أن تكتشف العمل الذي يتسبب بحالة الهلع في برنامجك في المستقبل وذلك بالنظر إلى القيم التي تتسبب بحالة الهلع ومن ثم النظر إلى الشيفرة البرمجية التي تسببت بها وتعديلها.
</p>

<p>
	سنعود لاحقًا إلى الماكرو <code>panic!‎</code> وإلى الحالات الواجب عدم استخدامها للتعامل مع الأخطاء في المقال التالي لاحقًا، إلا أننا سننتقل حاليًا إلى كيفية الحل من الأخطاء باستخدام <code>Result</code>.
</p>

<h2>
	الأخطاء القابلة للحل باستخدام Result
</h2>

<p>
	ليست معظم الأخطاء خطيرة وتتطلب إيقاف كامل البرنامج عند حدوثها، ففي بعض الأحيان يدل فشل عمل دالة ما على سبب ما يتطلب انتباهك واستجابتك له، فعلى سبيل المثال إذا فشلت عملية فتح ملف ما فهذا يعني غالبًا أن الملف الذي حددته غير موجود ولعلّك تفضّل إنشاء الملف وإعادة المحاولة بدلًا من إنهاء البرنامج كاملًا.
</p>

<p>
	تذكر أننا ذكرنا سابقًا في مقال <a href="https://academy.hsoub.com/programming/rust/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%84%D8%B9%D8%A8%D8%A9-%D8%AA%D8%AE%D9%85%D9%8A%D9%86-%D8%A7%D9%84%D8%A3%D8%B1%D9%82%D8%A7%D9%85-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1765/" rel="">برمجة لعبة تخمين الأرقام بلغة رست Rust</a> أن <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AA%D8%B9%D8%AF%D8%A7%D8%AF%D8%A7%D8%AA-enums-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1851/" rel="">المعدد enum</a> الذي يُدعى <code>Result</code> معرّف وداخله متغايرَان variants، هما <code>Ok</code> و <code>Err</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6562_15" style=""><span class="kwd">enum</span><span class="pln"> </span><span class="typ">Result</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">,</span><span class="pln"> E</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Ok</span><span class="pun">(</span><span class="pln">T</span><span class="pun">),</span><span class="pln">
    </span><span class="typ">Err</span><span class="pun">(</span><span class="pln">E</span><span class="pun">),</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمثّل كل من <code>T</code> و <code>E</code> معاملات نوع معمّم generic، وسنناقش الأنواع المعممة بتفصيل أكثر لاحقًا، ويكفي الآن معرفة أن <code>T</code> يمثّل نوع القيمة التي ستُعاد في حالة النجاح مع المتغاير <code>Ok</code>، بينما يمثل <code>E</code> نوع الخطأ الذي سيُعاد في حالة الفشل مع المتغاير <code>Err</code>، ولأن <code>Result</code> تحتوي على معاملات النوع المعمم فيمكننا استخدام النوع <code>Result</code> والدوال المعرفة ضمنها في العديد من الحالات، إذ تختلف كل من قيمة النجاح وقيمة الخطأ التي نريد أن نُعيدها.
</p>

<p>
	دعنا نستدعي دالةً تُعيد القيمة <code>Result</code> لأن الدالة قد تفشل. نحاول في الشيفرة 3 فتح ملف.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6562_17" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">fs</span><span class="pun">::</span><span class="typ">File</span><span class="pun">;</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let greeting_file_result </span><span class="pun">=</span><span class="pln"> </span><span class="typ">File</span><span class="pun">::</span><span class="pln">open</span><span class="pun">(</span><span class="str">"hello.txt"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 3: فتح ملف]
</p>

<p>
	النوع المُعاد من <code>File::open</code> هو <code>Result&lt;T, E&gt;‎</code>. يُملأ المعامل المعمّم <code>T</code> من خلال تنفيذ <code>File::open</code> مع نوع قيمة النجاح ألا وهي <code>std::fs::File</code> والتي تمثّل مقبض الملف file handle، بينما يُستخدم النوع <code>E</code> لتخزين قيمة الخطأ <code>std::io::Error</code>.
</p>

<p>
	يعني النوع المُعاد هذا أن استدعاء <code>File::open</code> قد ينجح ويُعيد مقبض ملف يمكن الكتابة إليه أو القراءة منه، إلا أن استدعاء الدالة قد يفشل في حال لم يكن الملف موجودًا على سبيل المثال، أو عند عدم توافر الصلاحيات المناسبة للوصول إليه؛ وبالتالي، ينبغي على الدالة <code>File::open</code> أن تمتلك القدرة على إخبارنا فيما إذا نجحت العملية ومنحنا مقبض الملف، أو إذا فشلت وتوفّر معلومات مناسبة عن الخطأ بنفس الوقت، وهذه هي المعلومات الموجودة فعلًا في المعدّد <code>Result</code>.
</p>

<p>
	ستكون قيمة المتغير <code>greeting_file_result</code> في حال نجاح <code>File::open</code> نسخةً من <code>Ok</code> تحتوي على مقبض الملف، وإلا فستكون قيمة المتغير <code>greeting_file_result</code> في حال الفشل نسخةً من <code>Err</code> تحتوي على المزيد من المعلومات حول نوع الخطأ الذي حدث.
</p>

<p>
	نحتاج الإضافة إلى الشيفرة 3 لتتخّذ بعض الإجراءات المختلفة بحسب قيمة <code>File::open</code> المُعادة، وتوضح الشيفرة 4 طريقة من الطرق للتعامل مع <code>Result</code> باستخدام أداة بسيطة ألا وهي تعبير <code>match</code> الذي ناقشناه سابقًا.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6562_19" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">fs</span><span class="pun">::</span><span class="typ">File</span><span class="pun">;</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let greeting_file_result </span><span class="pun">=</span><span class="pln"> </span><span class="typ">File</span><span class="pun">::</span><span class="pln">open</span><span class="pun">(</span><span class="str">"hello.txt"</span><span class="pun">);</span><span class="pln">

    let greeting_file </span><span class="pun">=</span><span class="pln"> match greeting_file_result </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Ok</span><span class="pun">(</span><span class="pln">file</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> file</span><span class="pun">,</span><span class="pln">
        </span><span class="typ">Err</span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> panic</span><span class="pun">!(</span><span class="str">"Problem opening the file: {:?}"</span><span class="pun">,</span><span class="pln"> error</span><span class="pun">),</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 4: استخدام تعبير match للتعامل مع متغايرات Result]
</p>

<p>
	لاحظ أن المعدد <code>Result</code> ومتغايراته -كما هو الحال مع المعدد <code>Option</code>- أُضيف إلى النطاق في بداية الشيفرة البرمجية، لذا ليس علينا تحديد <code>Result::‎</code> قبل المتغايرين <code>Ok</code> و <code>Err</code> في أذرع <code>match</code>.
</p>

<p>
	تُعيد الشيفرة البرمجية قيمة <code>file</code> الداخلية من المتغاير <code>Ok</code> عندما تكون النتيجة <code>Ok</code>، ومن ثم نُسند قيمة مقبض الملف إلى المتغير <code>greeting_file</code>، ومن ثم يمكننا استخدام مقبض الملف للقراءة منه أو الكتابة إليه بعد التعبير <code>match</code>.
</p>

<p>
	تتعامل الذراع الأخرى من <code>match</code> مع الحالات التي نحصل فيها على قيمة <code>Err</code> من <code>File::open</code>. استدعينا في هذا المثال الماكرو <code>panic!‎</code>، إذ سنحصل على الخرج التالي من الماكرو إذا لم يكن هناك أي ملف باسم "hello.txt" في المسار الحالي عند تشغيل الشيفرة البرمجية:
</p>

<pre class="ipsCode">$ cargo run
   Compiling error-handling v0.1.0 (file:///projects/error-handling)
    Finished dev [unoptimized + debuginfo] target(s) in 0.73s
     Running `target/debug/error-handling`
thread 'main' panicked at 'Problem opening the file: Os { code: 2, kind: NotFound, message: "No such file or directory" }', src/main.rs:8:23
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
</pre>

<p>
	يخبرنا الخرج عن الخطأ بالتحديد كما اعتدنا.
</p>

<h3>
	مطابقة عدة أخطاء
</h3>

<p>
	ستهلع الشيفرة 4 (أي ستتسبب باستدعاء الماكرو <code>panic!‎</code>) عند فشل <code>File::open</code> لأي سبب من الأسباب، إلا أنه من الممكن أن نتخذ إجراءات مختلفة لكل سبب من الأسباب: على سبيل المثال نريد إنشاء ملف وإعادة مقبضه إذا فشلت <code>File::open</code> بسبب عدم وجود الملف؛ وإلا فنريد الشيفرة أن تهلع باستخدام <code>panic!‎</code> إذا كان السبب مختلفًا -مثل عدم امتلاكنا للأذونات المناسبة- بالطريقة ذاتها في الشيفرة 4، ولتحقيق ذلك نُضيف تعبير <code>match</code> داخلي كما هو موضح في الشيفرة 5.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6562_21" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">fs</span><span class="pun">::</span><span class="typ">File</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">io</span><span class="pun">::</span><span class="typ">ErrorKind</span><span class="pun">;</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let greeting_file_result </span><span class="pun">=</span><span class="pln"> </span><span class="typ">File</span><span class="pun">::</span><span class="pln">open</span><span class="pun">(</span><span class="str">"hello.txt"</span><span class="pun">);</span><span class="pln">

    let greeting_file </span><span class="pun">=</span><span class="pln"> match greeting_file_result </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Ok</span><span class="pun">(</span><span class="pln">file</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> file</span><span class="pun">,</span><span class="pln">
        </span><span class="typ">Err</span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> match error</span><span class="pun">.</span><span class="pln">kind</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">ErrorKind</span><span class="pun">::</span><span class="typ">NotFound</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> match </span><span class="typ">File</span><span class="pun">::</span><span class="pln">create</span><span class="pun">(</span><span class="str">"hello.txt"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="typ">Ok</span><span class="pun">(</span><span class="pln">fc</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> fc</span><span class="pun">,</span><span class="pln">
                </span><span class="typ">Err</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> panic</span><span class="pun">!(</span><span class="str">"Problem creating the file: {:?}"</span><span class="pun">,</span><span class="pln"> e</span><span class="pun">),</span><span class="pln">
            </span><span class="pun">},</span><span class="pln">
            other_error </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                panic</span><span class="pun">!(</span><span class="str">"Problem opening the file: {:?}"</span><span class="pun">,</span><span class="pln"> other_error</span><span class="pun">);</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 5: التعامل بصورةٍ مختلفة مع أخطاء مختلفة]
</p>

<p>
	نوع القيمة التي تعيدها <code>File::open</code> داخل متغاير <code>Err</code> هو <code>io::Error</code> وهو <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%87%D9%8A%D8%A7%D9%83%D9%84-structs-%D9%84%D8%AA%D9%86%D8%B8%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1849/" rel="">هيكل struct</a> موجود في المكتبة القياسية، ويحتوي هذا الهيكل على التابع <code>kind</code> الذي يمكننا استدعاءه للحصول على القيمة <code>io::ErrorKind</code>. يحتوي المعدّد <code>io::ErrorKind</code> الموجود في المكتبة القياسية على متغايرات تمثل الأنواع المختلفة من الأخطاء التي قد تنتج من عملية <code>io</code>، والمتغاير الذي نريد استخدامه هنا هو <code>ErrorKind::NotFound</code> الذي يشير إلى الملف الذي نحاول فتحه إلا أنه غير موجود بعد، لذا نُطابقه مع <code>greeting_file_result</code> إلا أنه يوجد تعبير <code>match</code> داخلي خاص بالتابع <code>error.kind()‎</code>.
</p>

<p>
	الشرط الذي نريد أن نتحقق منه في تعبير <code>match</code> الداخلي هو فيما إذا كانت القيمة المُعادة من <code>error.kind()‎</code> هي المتغاير <code>NotFound</code> من المعدد <code>ErrorKind</code>، فإذا كان هذا الحال فعلًا فسنحاول إنشاء ملف باستخدام <code>File::create</code>، وإذا فشل <code>File::create</code> أيضًا، فنحن بحاجة ذراع آخر في تعبير <code>match</code> الداخلي، وعندما لا يكون من الممكن إنشاء الملف تُطبع رسالة خطأ مختلفة. تبقى ذراع <code>match</code> الخارجية الثانية كما هي حتى يهلع البرنامج عند حدوث أي خطأ ما عدا خطأ عدم العثور على الملف.
</p>

<h3>
	بدائل لاستخدام match مع Result<t e="">‎</t>
</h3>

<p>
	استخدمنا كثيرًا من تعابير <code>match</code>، فهي مفيدةٌ جدًا إلا أنها بدائية، وسنتحدث لاحقًا عن المغلفات closures التي تُستخدم مع الكثير من التوابع المعرّفة في <code>Result&lt;T, E&gt;‎</code>، وقد تكون هذه التوابع أكثر اختصارًا من <code>match</code> عند التعامل مع قيم <code>Result&lt;T, E&gt;‎</code> في شيفرتك البرمجية. على سبيل المثال، إليك طريقة أخرى لكتابة المنطق ذاته الموضح في الشيفرة 5، ولكن سنستخدم في هذه المرة المغلفات وتابع <code>unwrap_or_else</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6562_23" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">fs</span><span class="pun">::</span><span class="typ">File</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">io</span><span class="pun">::</span><span class="typ">ErrorKind</span><span class="pun">;</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let greeting_file </span><span class="pun">=</span><span class="pln"> </span><span class="typ">File</span><span class="pun">::</span><span class="pln">open</span><span class="pun">(</span><span class="str">"hello.txt"</span><span class="pun">).</span><span class="pln">unwrap_or_else</span><span class="pun">(|</span><span class="pln">error</span><span class="pun">|</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> error</span><span class="pun">.</span><span class="pln">kind</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="typ">ErrorKind</span><span class="pun">::</span><span class="typ">NotFound</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">File</span><span class="pun">::</span><span class="pln">create</span><span class="pun">(</span><span class="str">"hello.txt"</span><span class="pun">).</span><span class="pln">unwrap_or_else</span><span class="pun">(|</span><span class="pln">error</span><span class="pun">|</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                panic</span><span class="pun">!(</span><span class="str">"Problem creating the file: {:?}"</span><span class="pun">,</span><span class="pln"> error</span><span class="pun">);</span><span class="pln">
            </span><span class="pun">})</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            panic</span><span class="pun">!(</span><span class="str">"Problem opening the file: {:?}"</span><span class="pun">,</span><span class="pln"> error</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	على الرغم من أن هذه الشيفرة البرمجية تبدي السلوك ذاته الخاص بالشيفرة 5، إلا أنها لا تحتوي على أي تعبير <code>match</code> وهي أوضح للقراءة. ألقِ نظرةً على التابع <code>unwrap_or_else</code> وكيفية عمله في توثيق المكتبة القياسية إذا أردت وعُد مرةً ثانية لهذا المثال. تغنينا العديد من التوابع الأخرى عن الحاجة لاستخدام تعابير <code>match</code> متداخلة عند التعامل مع الأخطاء.
</p>

<h3>
	اختصارات للهلع عند حصول الأخطاء باستخدام unwrap و expect
</h3>

<p>
	يفي استخدام <code>match</code> بالغرض، إلا أن استخدامه يتطلب كتابة مطوّلة ولا يدلّ على الغرض منه بوضوح. بدلًا من ذلك يحتوي النوع <code>Result&lt;T, E&gt;‎</code> العديد من التوابع المساعدة المعرفة بداخله لإنجاز مهام متعددة ومحددة، إذ يمثّل التابع <code>unwrap</code> مثلًا تابعًا مختصرًا يؤدي مهمّة التعبير <code>match</code> الذي كتبناه في الشيفرة 4، فإذا كانت قيمة <code>Result</code> هي المتغاير <code>Ok</code>، فسيعيد القيمة الموجودة في <code>Ok</code> وإلا إذا احتوى على المتغاير <code>Err</code>، فسيستدعي الماكرو <code>panic!‎</code>. إليك مثالًا عمليًا على <code>unwrap</code>:
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6562_25" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">fs</span><span class="pun">::</span><span class="typ">File</span><span class="pun">;</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let greeting_file </span><span class="pun">=</span><span class="pln"> </span><span class="typ">File</span><span class="pun">::</span><span class="pln">open</span><span class="pun">(</span><span class="str">"hello.txt"</span><span class="pun">).</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إذا نفذت الشيفرة البرمجية السابقة دون وجود الملف hello.txt، سنحصل على رسالة خطأ من استدعاء <code>panic!‎</code> بسبب التابع <code>unwrap</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6562_27" style=""><span class="pln">thread </span><span class="str">'main'</span><span class="pln"> panicked at </span><span class="str">'</span><span class="pln">called </span><span class="pun">`</span><span class="typ">Result</span><span class="pun">::</span><span class="pln">unwrap</span><span class="pun">()`</span><span class="pln"> on an </span><span class="pun">`</span><span class="typ">Err</span><span class="pun">`</span><span class="pln"> value</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Os</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
code</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> kind</span><span class="pun">:</span><span class="pln"> </span><span class="typ">NotFound</span><span class="pun">,</span><span class="pln"> message</span><span class="pun">:</span><span class="pln"> </span><span class="str">"No such file or directory"</span><span class="pln"> </span><span class="pun">}</span><span class="str">',</span><span class="pln">
src</span><span class="pun">/</span><span class="pln">main</span><span class="pun">.</span><span class="pln">rs</span><span class="pun">:</span><span class="lit">4</span><span class="pun">:</span><span class="lit">49</span></pre>

<p>
	يسمح لنا التابع <code>expect</code> باختيار رسالة خطأ <code>panic!‎</code> بصورةٍ مشابهة، كما يمكن أن ينقل استخدام <code>expect</code> بدلًا من <code>unwrap</code> وتقديم رسالة خطأ معبّرة قصدك جيدًا، مما يساعدك في تعقب مصدر الهلع بصورةٍ أفضل. يمكننا استخدام <code>expect</code> على الشكل التالي:
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6562_29" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">fs</span><span class="pun">::</span><span class="typ">File</span><span class="pun">;</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let greeting_file </span><span class="pun">=</span><span class="pln"> </span><span class="typ">File</span><span class="pun">::</span><span class="pln">open</span><span class="pun">(</span><span class="str">"hello.txt"</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">expect</span><span class="pun">(</span><span class="str">"hello.txt should be included in this project"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نستخدم <code>expect</code> كما نستخدم <code>unwrap</code>، إما لإعادة مقبض الملف أو لاستدعاء الماكرو <code>panic!‎</code>. تمثل رسالة الخطأ التي سترسل باستخدام <code>expect</code> لاستدعاء <code>panic!‎</code> معاملًا يُمرّر إلى <code>expect</code> بدلًا من رسالة <code>panic!‎</code> الافتراضية التي يستخدمها التابع <code>unwrap</code>، إليك ما ستبدو عليه الرسالة:
</p>

<pre class="ipsCode">thread 'main' panicked at 'hello.txt should be included in this project: Os {
code: 2, kind: NotFound, message: "No such file or directory" }',
src/main.rs:5:10
</pre>

<p>
	يختار معظم مبرمجي لغة رست عند كتابة شيفرة برمجية مُخرجة جيدًا التابع <code>expect</code> بدلًا من <code>unwrap</code> لمنح السياق المناسب عن سبب نجاح العملية دومًا، ويمكنك بهذه الطريقة الحصول على معلومات أكثر لتستخدمها في تنقيح الأخطاء في حال كانت افتراضاتك خاطئة.
</p>

<h3>
	نشر الأخطاء
</h3>

<p>
	يُمكنك إعادة الخطأ الناتج عن استدعاء دالةٍ ما شيئًا قد يفشل إلى الشيفرة البرمجية المُستدعية له للتعامل مع الخطأ بدلًا من التعامل مع الخطأ داخل الدالة نفسها، وهذا يُعرف بنشر propagating الخطأ ويُعطي تحكمًا أكبر بالشيفرة البرمجية التي استدعت هذا الخطأ، إذ يمكننا توفير المزيد من المعلومات أو المنطق الذي يتعامل مع الخطأ بصورةٍ أفضل عما هو موجود في سياق شيفرتك البرمجية.
</p>

<p>
	على سبيل المثال، ألقِ نظرةً على الشيفرة 6 التي تقرأ اسم مستخدم من ملف، وتُعيد الدالة خطأ عدم وجود الملف أو عدم القدرة على قرائته إلى الشيفرة البرمجية التي استدعت الدالة.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6562_32" style=""><span class="com">#![allow(unused)]</span><span class="pln">
fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">fs</span><span class="pun">::</span><span class="typ">File</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">io</span><span class="pun">::{</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Read</span><span class="pun">};</span><span class="pln">

fn read_username_from_file</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Result</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">,</span><span class="pln"> io</span><span class="pun">::</span><span class="typ">Error</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let username_file_result </span><span class="pun">=</span><span class="pln"> </span><span class="typ">File</span><span class="pun">::</span><span class="pln">open</span><span class="pun">(</span><span class="str">"hello.txt"</span><span class="pun">);</span><span class="pln">

    let mut username_file </span><span class="pun">=</span><span class="pln"> match username_file_result </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Ok</span><span class="pun">(</span><span class="pln">file</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> file</span><span class="pun">,</span><span class="pln">
        </span><span class="typ">Err</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Err</span><span class="pun">(</span><span class="pln">e</span><span class="pun">),</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">

    let mut username </span><span class="pun">=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">();</span><span class="pln">

    match username_file</span><span class="pun">.</span><span class="pln">read_to_string</span><span class="pun">(&amp;</span><span class="pln">mut username</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Ok</span><span class="pun">(</span><span class="pln">_</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="typ">Ok</span><span class="pun">(</span><span class="pln">username</span><span class="pun">),</span><span class="pln">
        </span><span class="typ">Err</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="typ">Err</span><span class="pun">(</span><span class="pln">e</span><span class="pun">),</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 6: دالة تعيد الأخطاء إلى الشيفرة البرمجية التي استدعتها باستخدام تعبير match]
</p>

<p>
	يُمكن كتابة هذه الدالة بطريقة أقصر إلا أننا سنبدأ بكتابة معظمها يدويًا حتى نفهم التعامل مع الأخطاء أكثر، ثم سننظر إلى الطريقة الأقصر. دعنا ننظر إلى النوع المُعاد من الدالة أولًا ألا وهو <code>Result&lt;String, io::Error&gt;‎</code> وهذا يعني أن الدالة تُعيد قيمةً من النوع <code>Result&lt; T, E&gt;‎</code>، إذ يُملأ النوع المعمّم <code>T</code> بالنوع <code>String</code>، بينما يُملأ النوع المعمم <code>E</code> بالنوع <code>io::Error</code>.
</p>

<p>
	تحصل الشيفرة البرمجية التي استدعت الدالة في حال عمل الدالة دون أي مشاكل على القيمة <code>Ok</code> التي تخزِّن داخلها قيمةً من النوع <code>String</code> ألا وهو اسم المستخدم الذي قرأته الدالة من الملف، وإذا واجهت الدالة خلال عملها أي خطأ، تحصل الشيفرة البرمجية التي استدعت الدالة على القيمة <code>Err</code> التي تخزن داخلها نسخةً من <code>io::Error</code> تحتوي على مزيدٍ من المعلومات حول المشاكل التي جرت. اخترنا <code>io::Error</code> نوعًا للقيمة المُعادة لأنه يوافق نوع قيمة الخطأ المُعاد من كلا العمليتين التي نستدعي فيهما الدالة اللتان قد تفشلان ألا وهما الدالة <code>File::open</code> والتابع <code>read_to_string</code>.
</p>

<p>
	يبدأ متن الدالة باستدعاءٍ للدالة <code>File::open</code>، ثمّ نتعامل مع القيمة <code>Result</code> في <code>match</code> بطريقة مماثلة للتعبير <code>match</code> في الشيفرة 4؛ فإذا نجح عمل الدالة <code>File::open</code> يصبح مقبض الملف في متغير النمط <code>file</code> بقيمة المتغير القابل للتغيير <code>username_file</code> ويستمر تنفيذ الدالة، إلا أننا نستخدم الكلمة المفتاحية <code>return</code> في حالة <code>Err</code> عوضًا عن استدعاء <code>panic!</code> للخروج من الدالة وتمرير قيمة الخطأ الناتجة عن <code>File::open</code> في متغير النمط <code>e</code> إلى الشيفرة البرمجية التي استدعت الدالة.
</p>

<p>
	إذًا، تُنشئ الدالة قيمة <code>String</code> جديدة في المتغير <code>username</code> إذا كان لدينا مقبض ملف في <code>username_file</code>، ثم تستدعي التابع <code>read_to_string</code> باستخدام مقبض الملف في المتغير <code>username_file</code> لقراءة محتويات الملف إلى المتغير <code>username</code>.
</p>

<p>
	يعيد التابع <code>read_to_string</code> قيمة <code>Result</code> أيضًا لأنها من الممكن أن تفشل على الرغم من نجاح <code>File::open</code>، لذا فنحن بحاجة تعبير <code>match</code> آخر للتعامل مع قيمة <code>Result</code> على النحو التالي: تنجح دالتنا إذا نجح التابع <code>read_to_string</code> ونُعيد اسم المستخدم من الملف الموجود في <code>username</code> والمغلّف بقيمة <code>Ok</code>، وإلا إذا فشل <code>read_to_string</code> نُعيد قيمة الخطأ بطريقة إعادة الخطأ ذاتها في <code>match</code> التي تعاملت مع القيمة المُعادة من <code>File::open</code>، إلا أننا لسنا بحاجة كتابة الكلمة المفتاحية <code>return</code> هنا لأن هذا هو آخر تعبير في الدالة.
</p>

<p>
	ستتعامل الشيفرة البرمجية التي تستدعي هذه الشيفرة البرمجية مع حالة الحصول على قيمة <code>Ok</code> تحتوي على اسم مستخدم، أو قيمة <code>Err</code> تحتوي على قيمة من النوع <code>io::Error</code>، ويعود اختيار الإجراء المُتّخذ إلى الشيفرة البرمجية التي استدعت الدالة، فيمكن للشيفرة البرمجية أن تستدعي الماكرو <code>panic!‎</code> وأن توقف البرنامج فورًا في حال الحصول على قيمة <code>Err</code>، أو استخدام اسم مستخدم افتراضي، أو البحث على اسم المستخدم في مكان آخر عوضًا عن الملف. لا نمتلك ما يكفي من المعلومات حول الشيء الذي ستفعله الشيفرة البرمجية التي استدعت الدالة، لذا فنحن ننشر معلومات الخطأ أو النجاح للشيفرة البرمجية للتعامل معها بصورةٍ مناسبة.
</p>

<p>
	يُعد نمط نشر الأخطاء هذا شائع جدًا في رست، وتقدم لنا رست عامل إشارة الاستفهام <code>?</code> لاستخدام هذا النمط بسهولة.
</p>

<h4>
	اختصار لنشر الأخطاء: عامل ?
</h4>

<p>
	توضح الشيفرة 7 تطبيقًا للدالة <code>read_username_from_file</code> بوظيفة مماثلة للشيفرة 6، إلا أننا نستخدم هنا العامل <code>?</code>.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6562_34" style=""><span class="com">#![allow(unused)]</span><span class="pln">
fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">fs</span><span class="pun">::</span><span class="typ">File</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">io</span><span class="pun">::{</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Read</span><span class="pun">};</span><span class="pln">

fn read_username_from_file</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Result</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">,</span><span class="pln"> io</span><span class="pun">::</span><span class="typ">Error</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let mut username_file </span><span class="pun">=</span><span class="pln"> </span><span class="typ">File</span><span class="pun">::</span><span class="pln">open</span><span class="pun">(</span><span class="str">"hello.txt"</span><span class="pun">)?;</span><span class="pln">
    let mut username </span><span class="pun">=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">();</span><span class="pln">
    username_file</span><span class="pun">.</span><span class="pln">read_to_string</span><span class="pun">(&amp;</span><span class="pln">mut username</span><span class="pun">)?;</span><span class="pln">
    </span><span class="typ">Ok</span><span class="pun">(</span><span class="pln">username</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 7: دالة تعيد أخطاء للشيفرة البرمجية المُستدعية باستخدام العامل <code>?]</code>
</p>

<p>
	العامل <code>?</code> الموجود بعد القيمة <code>Result</code> مُعرَّف بحيث يعمل بطريقة مماثلة لعمل تعابير <code>match</code> التي عرفناها سابقًا بهدف التعامل مع قيم <code>Result</code> المختلفة في الشيفرة 6، فإذا كانت القيمة <code>Result</code> هي <code>Ok</code> تُعاد القيمة داخل <code>Ok</code> من التعبير هذا ويستمر تنفيذ البرنامج، بينما إذا كانت القيمة <code>Err</code> فتُعاد القيمة الموجودة داخل <code>Err</code> من الدالة ككُل وكأننا استخدمنا الكلمة المفتاحية <code>return</code> وبالتالي تُنشر قيمة الخطأ إلى الشيفرة البرمجية التي استدعت الدالة.
</p>

<p>
	هناك فرقٌ ما بين ما يفعله التعبير <code>match</code> في الشيفرة 6 وبين ما يفعله العامل <code>?</code>؛ إذ أن الأخطاء التي تُستدعى عن طريقة العامل <code>?</code> تمرّ بدالة <code>from</code>، المعرّفة في السمة <code>From</code> في المكتبة القياسية التي تُستخدم لتحويل القيم من نوع إلى نوع آخر؛ فعندما يستدعي العامل <code>?</code> الدالة <code>from</code> يُحوَّل الخطأ المُتلقى إلى نوع الخطأ المعرف في نوع القيمة المُعادة ضمن الدالة الحالية، وهذا الأمر مفيد عندما تُعيد الدالة نوعًا واحدًا من الخطأ لتمثيل جميع حالات فشل الدالة، حتى لو كانت الأجزاء التي قد تفشل ضمن الدالة تفشل لأسباب مختلفة.
</p>

<p>
	على سبيل المثال، يمكننا التعديل على الدالة <code>read_username_from_file</code> في الشيفرة 7 لتُعيد نوع خطأ مخصص نعرّفه اسمه <code>OurError</code>. إذا عرفنا أيضًا <code>impl From&lt;io::Error&gt; for OurError</code> لإنشاء نسخة من <code>OurError</code> من <code>io::Error</code> فهذا يعني أن العامل <code>?</code> المُستدعى في متن الدالة <code>read_username_from_file</code> سيستدعي <code>from</code> ويحوّل أنواع الأخطاء دون الحاجة لكتابة شيفرة برمجية إضافية لهذا الغرض.
</p>

<p>
	في سياق الشيفرة 7: سيُعيد العامل <code>?</code> في نهاية استدعاء <code>File::open</code> القيمة الموجودة داخل <code>Ok</code> إلى المتغير <code>username_file</code>، وإذا حدث خطأ ما فسيعيد العامل <code>?</code> قيمةً من <code>Err</code> إلى الشيفرة التي استدعت الدالة وتوقف تنفيذ الدالة مبكرًا، وينطبق الأمر ذاته على العامل <code>?</code> في نهاية استدعاء <code>read_to_string</code>.
</p>

<p>
	يُغنينا العامل <code>?</code> عن كتابة أي شيفرات برمجية متكررة ويجعل من كتابة الدالة عمليةً أسهل وأسرع، إلا أنه يمكننا جعل اللشيفرة البرمجية هذه أقصر أكثر عن طريق كتابة استدعاءات التوابع الواحدة تلو الأخرى مباشرةً بعد العامل <code>?</code> كما هو موضح في الشيفرة 8.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6562_36" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">fs</span><span class="pun">::</span><span class="typ">File</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">io</span><span class="pun">::{</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Read</span><span class="pun">};</span><span class="pln">

fn read_username_from_file</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Result</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">,</span><span class="pln"> io</span><span class="pun">::</span><span class="typ">Error</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let mut username </span><span class="pun">=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">();</span><span class="pln">

    </span><span class="typ">File</span><span class="pun">::</span><span class="pln">open</span><span class="pun">(</span><span class="str">"hello.txt"</span><span class="pun">)?.</span><span class="pln">read_to_string</span><span class="pun">(&amp;</span><span class="pln">mut username</span><span class="pun">)?;</span><span class="pln">

    </span><span class="typ">Ok</span><span class="pun">(</span><span class="pln">username</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 8: كتابة استدعاءات التوابع بصورة متسلسلة بعد العامل <code>?]</code>
</p>

<p>
	نقلنا عملية إنشاء <code>String</code> الجديد في <code>username</code> إلى بداية الدالة، ولم نغيّر ذلك الجزء، وبدلًا من إنشاء متغير <code>username_file</code>، كتبنا استدعاء <code>read_to_string</code> قبل نتيجة <code>File::open("hello.txt:)?‎</code> مباشرةً، إلا أن العامل <code>?</code> ما زال موجودًا في نهاية استدعاء <code>read_to_string</code> وما زلنا نُعيد قيمة <code>Ok</code> تحتوي على <code>username</code> عندما تنجح كل من <code>File::open</code> و <code>read_to_string</code> بدلًا من إعادة الأخطاء. وظيفة الشيفرة البرمجية مماثلة لكل من الشيفرة 6 والشيفرة 7 إلا أن الفارق هنا أن الشيفرة هذه أكثر سهولة للكتابة.
</p>

<p>
	توضح الشيفرة 9 طريقةً أكثر اختصارًا باستخدام <code>fs::read_to_string</code>.
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6562_38" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">fs</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">io</span><span class="pun">;</span><span class="pln">

fn read_username_from_file</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Result</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">,</span><span class="pln"> io</span><span class="pun">::</span><span class="typ">Error</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fs</span><span class="pun">::</span><span class="pln">read_to_string</span><span class="pun">(</span><span class="str">"hello.txt"</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 9: استخدام <code>fs::read_to_string</code> بدلًا من فتح وقراءة الملف بخطوتين منفصلتين]
</p>

<p>
	تُعد قراءة محتويات ملف ما وإسنادها إلى سلسلة نصية عمليةً شائعةً جدًا، لذا تُقدّم المكتبة القياسية دالةً لتحقيق هذه العملية ألا وهي <code>fs::read_to_string</code>، إذ تفتح الملف ومن ثم تُنشئ سلسلةً نصيةً <code>String</code> جديدة وتقرأ محتويات الملف وتُسندها إلى السلسلة النصية وتُعيد السلسلة النصية <code>String</code> أخيرًا، إلا أن استخدام <code>fs::read_to_string</code> لا يُتيح لنا إمكانية شرح جميع حالات التعامل مع الأخطاء، وهذا السبب في تقديمنا للطريقة الأطول أولًا.
</p>

<h4>
	أماكن استخدام العامل ?
</h4>

<p>
	يُمكن استخدام العامل <code>?</code> فقط في الدوال التي تُعيد نوعًا متوافقًا مع العامل <code>?</code>، وذلك لأن العامل <code>?</code> معرّف ليُجري عملية إعادة لقيمة بصورةٍ مبكرة خارج الدالة بطريقة تعبير <code>match</code> ذاتها الذي عرفناه في الشيفرة 6. نلاحظ في الشيفرة 6 أن <code>match</code> استخدم القيمة <code>Result</code> وأعاد الذراع القيمة <code>Err(e)‎</code>، ينبغي على النوع المُعاد من الدالة أن يكون <code>Result</code> لكي يكون متوافقًا مع التعليمة <code>return</code> هذه.
</p>

<p>
	دعنا ننظر في الشيفرة 10 إلى الخطأ الذي سنحصل عليه في حال استخدمنا العامل <code>?</code> في الدالة <code>main</code> بنوع مُعاد غير متوافق مع الأنواع الخاصة بالعامل <code>?</code>:
</p>

<p>
	اسم الملف: src/main.rs
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6562_40" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">fs</span><span class="pun">::</span><span class="typ">File</span><span class="pun">;</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let greeting_file </span><span class="pun">=</span><span class="pln"> </span><span class="typ">File</span><span class="pun">::</span><span class="pln">open</span><span class="pun">(</span><span class="str">"hello.txt"</span><span class="pun">)?;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 10: محاولة استخدام العامل <code>?</code> في الدالة <code>main</code> التي تُعيد <code>()</code> وهي قيمة غير متوافقة]
</p>

<p>
	تفتح الشيفرة البرمجية السابقة ملفًا، وقد تفشل عملية فتحه. يتبع العامل <code>?</code> القيمة <code>Result</code> المُعادة من <code>File::open</code> إلا أن الدالة <code>main</code> تحتوي على النوع المُعاد <code>()</code> وليس <code>Result</code>، وعندما نصرّف الشيفرة البرمجية السابقة نحصل على رسالة الخطأ التالية:
</p>

<pre class="ipsCode">$ cargo run
   Compiling error-handling v0.1.0 (file:///projects/error-handling)
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
 --&gt; src/main.rs:4:48
  |
3 | / fn main() {
4 | |     let greeting_file = File::open("hello.txt")?;
  | |                                                ^ cannot use the `?` operator in a function that returns `()`
5 | | }
  | |_- this function should return `Result` or `Option` to accept `?`
  |
  = help: the trait `FromResidual&lt;Result&lt;Infallible, std::io::Error&gt;&gt;` is not implemented for `()`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `error-handling` due to previous error
</pre>

<p>
	يشير هذا الخطأ إلى أنه من غير المسموح استخدام العامل <code>?</code> إلا في دالة تُعيد <code>Result</code>، أو <code>Option</code>، أو أي نوع آخر يطبّق <code>FromResidual</code>.
</p>

<p>
	يوجد خياران لإصلاح الخطأ السابق: الأول هو تغيير نوع القيمة المُعادة من الدالة لتصبح متوافقةً مع القيمة التي تستخدم العامل <code>?</code> عليها وهذا خيار جيّد طالما لا يوجد أي قيود أخرى تمنعك من ذلك، أما الخيار الثاني فهو باستخدام <code>match</code> أو إحدى توابع <code>Result&lt;T, E&gt;‎</code> للتعامل مع <code>Result&lt;T, E&gt;</code>‎ بالطريقة المناسبة.
</p>

<p>
	ذكرت رسالة الخطأ أيضًا أنه يمكننا استخدام العامل <code>?</code> مع قيم <code>Option&lt;T&gt;‎</code> أيضًا بالطريقة ذاتها التي نستخدم فيها العامل مع <code>Result</code>، إلا أنه يمكنك استخدام العامل على <code>Option</code> فقط في حال كانت الدالة تُعيد <code>Option</code>. يشبه سلوك العامل <code>?</code> عند استدعائه على <code>Option&lt;T, E&gt;‎</code> سلوكه عند استدعائه على <code>Result&lt;T, E&gt;‎</code>، إذ تُعاد القيمة <code>None</code> كما هي مبكرًا من الدالة، وإذا كانت القيمة <code>Some</code> فالقيمة التي بداخل <code>Some</code> هي القيمة الناتجة عن ذلك التعبير، وتستمر الدالة عندها بالتنفيذ. تحتوي الشيفرة 11 على مثال لدالة تعثر على المحرف الأخير من السطر الأول في سلسلة نصية.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6562_43" style=""><span class="pln">fn last_char_of_first_line</span><span class="pun">(</span><span class="pln">text</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Option</span><span class="str">&lt;char&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    text</span><span class="pun">.</span><span class="pln">lines</span><span class="pun">().</span><span class="pln">next</span><span class="pun">()?.</span><span class="pln">chars</span><span class="pun">().</span><span class="pln">last</span><span class="pun">()</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 11: استخدام العامل <code>?</code> على قيمة <code>Option&lt;T&gt;‎]</code>
</p>

<p>
	تُعيد الدالة <code>Option&lt;char&gt;‎</code> لأنه من الممكن أن يكون هناك محرف في النتيجة أو لا. تأخذ الشيفرة البرمجية السابقة شريحة السلسلة النصية string slice‏ <code>text</code> وسيطًا، وتستدعي التابع <code>lines</code> عليها مما يُعيد مُكرّرًا iterator عبر السطور في السلسلة النصية.
</p>

<p>
	ولأن هذه الدالة تهدف لفحص السطر الأول فهي تستدعي <code>next</code> على المكرر للحصول على القيمة الأولى منه، وإذا كان <code>text</code> سلسلة نصية فارغة فسيُعيد استدعاء <code>next</code> القيمة <code>None</code> وفي هذه الحالة نستخدم العامل <code>?</code> لإيقاف التنفيذ وإعادة القيمة <code>None</code> من الدالة <code>last_char_of_first_line</code>. إذا لم يكن <code>text</code> سلسلةً نصيةً فارغة فسيُعيد استدعاء <code>next</code> قيمة <code>Some</code> تحتوي على شريحة سلسلة نصية تحتوي على السطر الأول من <code>text</code>.
</p>

<p>
	يستخلص العامل <code>?</code> شريحة السلسلة النصية ويمكننا استدعاء <code>chars</code> على شريحة السلسلة النصية للحصول على مكرّر يحتوي على محارفه. ما نبحث عنه هنا هو المحرف الأخير من السطر الأول، لذلك نستدعي <code>last</code> للحصول على آخر عنصر موجود في المكرر وهي قيمة <code>Option</code> لأنه من الممكن أن يكون السطر الأول سلسلة نصية فارغة، إذا من الممكن مثلًا أن يبدأ <code>text</code> بسطر فارغ وأن يحتوي على محارف في السطور الأخرى مثل <code>"‎\nhi"</code>، فإذا كان هناك فعلًا محرف في نهاية السطر فإننا نحصل عليه داخل متغاير <code>Some</code>.
</p>

<p>
	يُعطينا العامل <code>?</code> الموجود في المنتصف طريقة موجزة للتعبير عن هذا المنطق مما يسمح لنا بتطبيق محتوى الدالة بسطر واحد، وإذا لم نستطع تطبيق العامل <code>?</code> على <code>Option</code>، سيتوجب علينا كتابة المنطق ذاته باستخدام عدد أكبر من استدعاءات للدوال أو باستخدام التعبير <code>match</code>.
</p>

<p>
	لاحظ أنه يمكنك استخدام العامل <code>?</code> على <code>Result</code> داخل دالة تُعيد <code>Result</code>، ويمكنك استخدام العامل <code>?</code> على <code>Option</code> داخل دالة تُعيد <code>Option</code> إلا أنه لا يمكنك الخلط ما بين الاثنين، إذ لن يحول العامل <code>?</code> النوع <code>Result</code> إلى <code>Option</code> أو بالعكس تلقائيًا، ويمكنك في هذه الحالات <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D9%88%D8%A7%D8%A8%D8%B9-methods-%D8%B6%D9%85%D9%86-%D8%A7%D9%84%D9%87%D9%8A%D8%A7%D9%83%D9%84-structs-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1850/" rel="">استخدام تواب</a>ع، مثل التابع <code>ok</code> على النوع <code>Result</code>، أو التابع <code>ok_or</code> على النوع <code>Option</code> لإجراء التحويل صراحةً.
</p>

<p>
	استخدمت جميع دوال <code>main</code> حتى هذه اللحظة القيمة المُعادة <code>()</code>. تُعد الدالة <code>main</code>مميزةً لأنها نقطة بداية ونهاية البرامج التنفيذية وبالتالي هناك بعض القيود على الأنواع المُعادة لكي تتصرف البرامج على النحو الصحيح كما هو متوقع.
</p>

<p>
	لحسن الحظ، تُعيد الدالة <code>main</code> النوع <code>Result&lt;(), E&gt;‎</code>. تحتوي الشيفرة 12 على الشيفرة البرمجية الموجودة في الشيفرة 10، إلا أننا عدلنا النوع المُعاد من الدالة <code>main</code> ليصبح <code>Result&lt;(), Box&lt;dyn Error&gt;&gt;‎</code> وأضفنا قيمة مُعادة <code>Ok(())‎</code> في النهاية، وستُصرَّف الشيفرة البرمجية نتيجةً لهذه التعديلات بنجاح:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6562_45" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">error</span><span class="pun">::</span><span class="typ">Error</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">fs</span><span class="pun">::</span><span class="typ">File</span><span class="pun">;</span><span class="pln">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Result</span><span class="pun">&lt;(),</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">Error</span><span class="pun">&gt;&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let greeting_file </span><span class="pun">=</span><span class="pln"> </span><span class="typ">File</span><span class="pun">::</span><span class="pln">open</span><span class="pun">(</span><span class="str">"hello.txt"</span><span class="pun">)?;</span><span class="pln">

    </span><span class="typ">Ok</span><span class="pun">(())</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 12: تعديل الدالة <code>main</code> لتُعيد <code>Result&lt;(), E&gt;‎</code> لتسمح لنا باستخدام العامل <code>?</code> على قيم <code>Result]</code>
</p>

<p>
	النوع <code>Box&lt;dyn Error&gt;‎</code> هو كائن سمة trait object وهو ما سنغطيه لاحقًا، ويكفي الآن معرفتك أن <code>Box&lt;dyn Error&gt;‎</code> يعني "أي نوع من الأخطاء". استخدام العامل <code>?</code> على قيمة <code>Result</code> في دالة <code>main</code> باستخدام قيمة الخطأ <code>Box&lt;dyn Error&gt;‎</code> هو أمر مسموح، وذلك لأنه يسمح لأي نوع <code>Err</code> أن يُعاد مبكرًا، وعلى الرغم من أن محتوى الدالة <code>main</code> سيعيد الأخطاء من النوع <code>std::io::Error</code> فقط إلا أن بصمة الدالة ستبقى صالحة بتحديد <code>Box&lt;dyn Error&gt;‎</code> إذا أُضيفت شيفرة برمجية تُعيد أخطاء أخرى داخل الدالة <code>main</code>.
</p>

<p>
	يتوقف الملف التنفيذي عندما تُعيد الدالة <code>main</code> القيمة <code>Result&lt;(), E&gt;‎</code> وذلك بإعادة القيمة "0" إذا أعادت <code>main</code> القيمة <code>Ok(())‎</code> وقيمة غير صفرية إذا أعادت الدالة قيمة <code>Err</code>. تُعيد الملفات التنفيذية المكتوبة بلغة سي أعدادًا صحيحة عند مغادرة البرنامج؛ فالبرنامج الذي يتوقف بنجاح يُعيد العدد الصحيح "0"؛ بينما يُعيد البرنامج الذي يتوقف بسبب خطأ قيمة عدد صحيح لا تساوي "0". تُعيد رست أيضًا أعدادًا صحيحة من الملفات التنفيذية بصورةٍ مماثلة لهذا الاصطلاح.
</p>

<p>
	قد تُعيد الدالة <code>main</code> أي نوع يطبّق <a href="https://doc.rust-lang.org/stable/std/process/trait.Termination.html" rel="external nofollow">السمة <code>std::process::Termination</code></a> التي تحتوي بدورها على دالة تدعى <code>report</code> تُعيد قيمة <code>ExitCode</code>، انظر إلى توثيق المكتبة القياسية للمزيد من المعلومات حول استخدام سمة <code>Termination</code> ضمن أنواعك.
</p>

<p>
	الآن، بعد مناقشتنا التفاصيل الخاصة بالماكرو <code>panic!‎</code> وإعادة النوع <code>Result</code>، سنعود تاليًا إلى موضوع كيفية تحديد الاستخدام المناسب لكل حالة.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch09-00-error-handling.html" rel="external nofollow">Error Handling</a> من كتاب <a href="https://doc.rust-lang.org/stable/book/title-page.html" rel="external nofollow">The Rust Programming Language</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D9%8A%D8%A7%D8%B1-%D9%85%D8%A7-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%A7%D9%83%D8%B1%D9%88-panic%E2%80%8E-%D9%88%D8%A7%D9%84%D9%86%D9%88%D8%B9-result-%D9%84%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-rust-r1921/" rel="">الاختيار ما بين الماكرو panic!‎ والنوع Result للتعامل مع الأخطاء في لغة Rust</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%86%D9%88%D8%B9-hashmap-%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%B1%D8%B3%D8%AA-rust-r1917/" rel="">كيفية استخدام النوع HashMap لتخزين البيانات في رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-data-types-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1780/" rel="">أنواع البيانات Data Types في لغة رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%AD%D8%B2%D9%85-packages-%D9%88%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B5%D8%B1%D9%81%D8%A9-crates-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r1853/" rel="">الحزم packages والوحدات المصرفة crates في لغة رست Rust</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%A7%D9%84%D8%B3%D8%A8%D8%B9-%D8%A7%D9%84%D9%82%D8%A7%D8%AA%D9%84%D8%A9-%D9%84%D8%A3%D9%8A%D9%91-%D9%85%D8%B4%D8%B1%D9%88%D8%B9-%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA-r742/" rel="">الأخطاء السبع القاتلة لأيّ مشروع برمجيات</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1920</guid><pubDate>Thu, 09 Mar 2023 13:01:00 +0000</pubDate></item></channel></rss>
