<?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/?d=2</link><description>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x644;&#x63A;&#x629; Rust</description><language>ar</language><item><title>&#x628;&#x646;&#x627;&#x621; &#x62E;&#x627;&#x62F;&#x645; &#x648;&#x64A;&#x628; &#x645;&#x62A;&#x639;&#x62F;&#x62F; &#x645;&#x647;&#x627;&#x645; &#x627;&#x644;&#x645;&#x639;&#x627;&#x644;&#x62C;&#x629; &#x628;&#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A; - &#x627;&#x644;&#x62C;&#x632;&#x621; &#x627;&#x644;&#x62B;&#x627;&#x644;&#x62B;</title><link>https://academy.hsoub.com/programming/rust/%D8%A8%D9%86%D8%A7%D8%A1-%D8%AE%D8%A7%D8%AF%D9%85-%D9%88%D9%8A%D8%A8-%D9%85%D8%AA%D8%B9%D8%AF%D8%AF-%D9%85%D9%87%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%AB%D8%A7%D9%84%D8%AB-r2157/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_11/--------Rust-(-).png.6a13c7b1ec0b9733dbbe80825c6decaa.png" /></p>
<p>
	بدأنا في مقال <a href="https://academy.hsoub.com/programming/rust/%D8%A8%D9%86%D8%A7%D8%A1-%D8%AE%D8%A7%D8%AF%D9%85-%D9%88%D9%8A%D8%A8-%D9%85%D8%AA%D8%B9%D8%AF%D8%AF-%D9%85%D9%87%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%A3%D9%88%D9%84-r2150/" rel="">الجزء الأول</a> ببناء مشروع عملي بلغة رست وهو عبارة عن خادم ويب متعدد مهام المعالجة، إذ بنينا الخادم الأساسي وكان أحادي خيط المعالجة، وعملنا في مقال <a href="https://academy.hsoub.com/programming/rust/%D8%A8%D9%86%D8%A7%D8%A1-%D8%AE%D8%A7%D8%AF%D9%85-%D9%88%D9%8A%D8%A8-%D9%85%D8%AA%D8%B9%D8%AF%D8%AF-%D9%85%D9%87%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%AB%D8%A7%D9%86%D9%8A-r2151/" rel="">الجزء الثاني</a> على تحويله إلى خادم متعدد خيوط المعالجة، وسننهي في هذا المقال بناء الخادم ليصبح جاهزًا، فإذا لم تكن قرأت المقالات السابقة، فارجع لها قبل قراءة هذا المقال.
</p>

<h2 id="">
	الإغلاق الرشيق وتحرير الذاكرة
</h2>

<p>
	تستجيب الشيفرة 20 للطلبات بصورةٍ غير متزامنة عبر استخدام مجمع خيط كما نريد، إذ نحصل على بعض التحذيرات من حقول <code>workers</code> و <code>id</code> و <code>thread</code> التي لن نستخدمها مباشرةً وتذكرنا أننا لم نحرر أي شيء من الذاكرة. عندما نستخدم الحل البدائي الذي هو استخدام مفتاحي "ctrl-c" لإيقاف الخيط الرئيسي، تتوقف الخيوط مباشرةً حتى لو كانوا يخدّمون طلبًا.
</p>

<p>
	سننفّذ سمة <code>Drop</code> لاستدعاء <code>join</code> على كل خيط في المجمع لكي ننهي الطلبات التي تعمل قبل الإغلاق، ثم سننفّذ طريقةً لإخبار الخيوط ألا تقبل طلبات جديدة قبل الإغلاق. لرؤية عمل هذا الكود سنعدّل الخادم ليقبل طلبين فقط قبل أن يغلق <a href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%88%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D8%AA%D9%88%D8%A7%D8%B2%D9%8A-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1485/" rel="">مجمع الخيط thread pool</a>.
</p>

<h3 id="drop">
	تنفيذ سمة Drop على مجمع خيط
</h3>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3018_8" style=""><span class="pln">impl </span><span class="typ">Drop</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn drop</span><span class="pun">(&amp;</span><span class="pln">mut self</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"> worker in </span><span class="pun">&amp;</span><span class="pln">mut self</span><span class="pun">.</span><span class="pln">workers </span><span class="pun">{</span><span class="pln">
            println</span><span class="pun">!(</span><span class="str">"Shutting down worker {}"</span><span class="pun">,</span><span class="pln"> worker</span><span class="pun">.</span><span class="pln">id</span><span class="pun">);</span><span class="pln">

            worker</span><span class="pun">.</span><span class="pln">thread</span><span class="pun">.</span><span class="pln">join</span><span class="pun">().</span><span class="pln">unwrap</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;">
	[الشيفرة 22: ضم كل خيط عندما يخرج المجمع خارج النطاق]
</p>

<p>
	أولًا، نمرّ على كل <code>workers</code> في مجمع الخيط، واستُخدمت <code>‎&amp;mut</code> هنا لأن <code>self</code> هو مرجع متغيّر، ونريد أيضًا تغيير <code>worker</code>. نطبع لكل عامل رسالةً تقول أن هذا العامل سيُغلق، ثم نستدعي <code>join</code> على خيط العمال. إذا فشل استدعاء <code>join</code> نستخدم <code>unwrap</code> لجعل رست تهلع وتذهب إلى إغلاق غير رشيق.
</p>

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

<pre class="ipsCode">$ cargo check
    Checking hello v0.1.0 (file:///projects/hello)
error[E0507]: cannot move out of `worker.thread` which is behind a mutable reference
  --&gt; src/lib.rs:52:13
   |
52 |             worker.thread.join().unwrap();
   |             ^^^^^^^^^^^^^ ------ `worker.thread` moved due to this method call
   |             |
   |             move occurs because `worker.thread` has type `JoinHandle&lt;()&gt;`, which does not implement the `Copy` trait
   |
note: this function takes ownership of the receiver `self`, which moves `worker.thread`
  --&gt; /rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/std/src/thread/mod.rs:1581:17

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

<p>
	يوضّح الخطأ أننا لا يمكن أن نستدعي <code>join</code> لأنه لدينا استعارة متغيرة على كل <code>worker</code> وتأخذ <code>join</code> ملكية وسطائها، ولمعالجة هذه المشكلة نحن بحاجة لنقل الخيط خارج نسخة <code>Worker</code> التي تملك <code>thread</code> حتى تستطيع <code>join</code> استهلاك الخيط، وقد فعلنا ذلك في الشيفرة 15 من المقال <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D9%86%D9%85%D8%B7-%D8%AA%D8%B5%D9%85%D9%8A%D9%85%D9%8A-design-pattern-%D9%83%D8%A7%D8%A6%D9%86%D9%8A-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-object-oriented-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2108/" rel="">تنفيذ نمط تصميمي Design Pattern كائني التوجه Object-Oriented في لغة رست</a>. إذا احتفظ <code>Worker</code> بـ <code>Option&lt;thread::JoinHandle&lt;()&gt;&gt;‎</code>، يمكننا استدعاء تابع <code>take</code> على <code>Option</code> لنقل القيمة خارج المتغاير <code>Some</code> وإبقاء المتغاير <code>None</code> في مكانه، بمعنى آخر سيحتوي <code>Worker</code> عامل على متغاير <code>Some</code> في <code>Thread</code> الخاص به وعندما نريد تحرير ذاكرة <code>Worker</code> نستبدل <code>Some</code> بالقيمة <code>None</code> حتى لا يوجد لدى <code>Worker</code> أي خيط لينفذه.
</p>

<p>
	لذا نحن نعرف أننا نريد تحديث تعريف <code>Worker</code> على النحو التالي.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3018_11" style=""><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Worker</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    id</span><span class="pun">:</span><span class="pln"> usize</span><span class="pun">,</span><span class="pln">
    thread</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Option</span><span class="pun">&lt;</span><span class="pln">thread</span><span class="pun">::</span><span class="typ">JoinHandle</span><span class="pun">&lt;()&gt;&gt;,</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<pre class="ipsCode">$ cargo check
    Checking hello v0.1.0 (file:///projects/hello)
error[E0599]: no method named `join` found for enum `Option` in the current scope
  --&gt; src/lib.rs:52:27
   |
52 |             worker.thread.join().unwrap();
   |                           ^^^^ method not found in `Option&lt;JoinHandle&lt;()&gt;&gt;`
   |
note: the method `join` exists on the type `JoinHandle&lt;()&gt;`
  --&gt; /rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/std/src/thread/mod.rs:1581:5
help: consider using `Option::expect` to unwrap the `JoinHandle&lt;()&gt;` value, panicking if the value is an `Option::None`
   |
52 |             worker.thread.expect("REASON").join().unwrap();
   |                          +++++++++++++++++

error[E0308]: mismatched types
  --&gt; src/lib.rs:72:22
   |
72 |         Worker { id, thread }
   |                      ^^^^^^ expected enum `Option`, found struct `JoinHandle`
   |
   = note: expected enum `Option&lt;JoinHandle&lt;()&gt;&gt;`
            found struct `JoinHandle&lt;_&gt;`
help: try wrapping the expression in `Some`
   |
72 |         Worker { id, thread: Some(thread) }
   |                      +++++++++++++      +

Some errors have detailed explanations: E0308, E0599.
For more information about an error, try `rustc --explain E0308`.
error: could not compile `hello` due to 2 previous errors
</pre>

<p>
	لنعالج الخطأ الثاني الذي يشير إلى الشيفرة في نهاية <code>Worker::new</code>، إذ نريد تغليف قيمة <code>thread</code> في <code>Some</code> عندما ننشئ <code>Worker</code> جديد. أجرِ الخطوات التالية لتصحيح هذا الخطأ:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3018_14" style=""><span class="pln">impl </span><span class="typ">Worker</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">id</span><span class="pun">:</span><span class="pln"> usize</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Arc</span><span class="pun">&lt;</span><span class="typ">Mutex</span><span class="pun">&lt;</span><span class="pln">mpsc</span><span class="pun">::</span><span class="typ">Receiver</span><span class="pun">&lt;</span><span class="typ">Job</span><span class="pun">&gt;&gt;&gt;)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Worker</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">Worker</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            id</span><span class="pun">,</span><span class="pln">
            thread</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">thread</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>Drop</code>، وذكرنا سابقًا أننا أردنا استدعاء <code>take</code> على قيمة <code>Option</code> لنقل <code>thread</code> خارج <code>worker</code>. أجرِ التغييرات التالية لتصحيح هذا الخطأ:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3018_16" style=""><span class="pln">impl </span><span class="typ">Drop</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn drop</span><span class="pun">(&amp;</span><span class="pln">mut self</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"> worker in </span><span class="pun">&amp;</span><span class="pln">mut self</span><span class="pun">.</span><span class="pln">workers </span><span class="pun">{</span><span class="pln">
            println</span><span class="pun">!(</span><span class="str">"Shutting down worker {}"</span><span class="pun">,</span><span class="pln"> worker</span><span class="pun">.</span><span class="pln">id</span><span class="pun">);</span><span class="pln">

            </span><span class="kwd">if</span><span class="pln"> let </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">thread</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> worker</span><span class="pun">.</span><span class="pln">thread</span><span class="pun">.</span><span class="pln">take</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                thread</span><span class="pun">.</span><span class="pln">join</span><span class="pun">().</span><span class="pln">unwrap</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>
	كما تحدثنا سابقًا في المقال <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-oop-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2097/" rel="">البرمجة كائنية التوجه <abbr title="Object-Oriented Programming | البرمجة كائنية التوجه">OOP</abbr> في لغة رست</a>، يأخذ التابع <code>take</code> على <code>Option</code> المتغاير <code>Some</code> خارجًا ويبقي <code>None</code> بدلًا عنه. استخدمنا <code>if let</code> لتفكيك <code>Some</code> والحصول على الخيط، ثم استدعينا <code>join</code> على الخيط. إذا كان خيط العامل هو أساسًا <code>None</code> نعرف أن العامل قد حرًر ذاكرته ولا يحصل شيء في هذه الحالة.
</p>

<h3 id="-1">
	الإشارة للخيط ليتوقف عن الاستماع إلى الوظائف
</h3>

<p>
	تُصرّف الشيفرة بدون تحذيرات بعد كل التغييرات التي أجريناها، ولكن الخبر السيء أنها لا تعمل كما أردنا. النقطة المهمة هي في منطق المغلفات المنفذة بواسطة خيوط نسخ <code>Worker</code>، إذ نستدعي حتى اللحظة <code>join</code> لكن لا تُغلق الخيوط لأننها تعمل في <code>loop</code> للأبد بحثًا عن وظائف. إذا أسقطنا <code>Threadpool</code> بتنفيذنا الحالي للسمة <code>drop</code>، سيُمنع الخيط الأساسي للأبد بانتظار الخيط الأول حتى ينتهي، ولحل هذه المشكلة نحتاج لتغيير تنفيذ <code>drop</code> في <code>ThreadPool</code>، ثم إجراء تغيير في حلقة <code>Worker</code>.
</p>

<p>
	أولًا، سنغير تنفيذ <code>drop</code> في <code>ThreadPool</code> ليسقِط صراحةً <code>sender</code> قبل انتظار الخيوط لتنتهي. تظهر الشيفرة 23 التغييرات في <code>ThreadPool</code> لتسقط صراحةً <code>sender</code>. استخدمنا نفس تقنيات<code>Option</code> و <code>take</code> كما فعلنا مع الخيط لكي يستطيع نقل <code>sender</code> خارج <code>ThreadPool</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3018_18" style=""><span class="pln">pub </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    workers</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">Worker</span><span class="pun">&gt;,</span><span class="pln">
    sender</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Option</span><span class="pun">&lt;</span><span class="pln">mpsc</span><span class="pun">::</span><span class="typ">Sender</span><span class="pun">&lt;</span><span class="typ">Job</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">
impl </span><span class="typ">ThreadPool</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">size</span><span class="pun">:</span><span class="pln"> usize</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">ThreadPool</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">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            workers</span><span class="pun">,</span><span class="pln">
            sender</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">sender</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 fn execute</span><span class="pun">&lt;</span><span class="pln">F</span><span class="pun">&gt;(&amp;</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="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">+</span><span class="pln"> </span><span class="typ">Send</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'</span><span class="kwd">static</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        let job </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="pln">f</span><span class="pun">);</span><span class="pln">

        self</span><span class="pun">.</span><span class="pln">sender</span><span class="pun">.</span><span class="pln">as_ref</span><span class="pun">().</span><span class="pln">unwrap</span><span class="pun">().</span><span class="pln">send</span><span class="pun">(</span><span class="pln">job</span><span class="pun">).</span><span class="pln">unwrap</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="typ">Drop</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn drop</span><span class="pun">(&amp;</span><span class="pln">mut self</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        drop</span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">sender</span><span class="pun">.</span><span class="pln">take</span><span class="pun">());</span><span class="pln">

        </span><span class="kwd">for</span><span class="pln"> worker in </span><span class="pun">&amp;</span><span class="pln">mut self</span><span class="pun">.</span><span class="pln">workers </span><span class="pun">{</span><span class="pln">
            println</span><span class="pun">!(</span><span class="str">"Shutting down worker {}"</span><span class="pun">,</span><span class="pln"> worker</span><span class="pun">.</span><span class="pln">id</span><span class="pun">);</span><span class="pln">

            </span><span class="kwd">if</span><span class="pln"> let </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">thread</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> worker</span><span class="pun">.</span><span class="pln">thread</span><span class="pun">.</span><span class="pln">take</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                thread</span><span class="pun">.</span><span class="pln">join</span><span class="pun">().</span><span class="pln">unwrap</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;">
	[الشيفرة 23: إسقاط <code>sender</code> صراحةً قبل جمع الخيوط الفعالة]
</p>

<p>
	يغلق إسقاط <code>sender</code> القناة، وهذا يشير بدوره إلى عدم إرسال أي رسائل إضافية، وعندما نفعل ذلك تعيد كل الاستدعاءات إلى <code>recv</code> التي تجريها الخيوط الفعالة في الحلقة اللانهائية خطأً. نغير حلقة <code>Worker</code> في الشيفرة 24 لتخرج من الحلقة برشاقة في تلك الحالة، يعني أن الخيوط ستنتهي عندما يستدعي <code>join</code> عليهم في تنفيذ <code>drop</code> في <code>ThreadPool</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3018_20" style=""><span class="pln">impl </span><span class="typ">Worker</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">id</span><span class="pun">:</span><span class="pln"> usize</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Arc</span><span class="pun">&lt;</span><span class="typ">Mutex</span><span class="pun">&lt;</span><span class="pln">mpsc</span><span class="pun">::</span><span class="typ">Receiver</span><span class="pun">&lt;</span><span class="typ">Job</span><span class="pun">&gt;&gt;&gt;)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Worker</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let thread </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"> loop </span><span class="pun">{</span><span class="pln">
            let message </span><span class="pun">=</span><span class="pln"> receiver</span><span class="pun">.</span><span class="pln">lock</span><span class="pun">().</span><span class="pln">unwrap</span><span class="pun">().</span><span class="pln">recv</span><span class="pun">();</span><span class="pln">

            match message </span><span class="pun">{</span><span class="pln">
                </span><span class="typ">Ok</span><span class="pun">(</span><span class="pln">job</span><span class="pun">)</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">"Worker {id} got a job; executing."</span><span class="pun">);</span><span class="pln">

                    job</span><span class="pun">();</span><span class="pln">
                </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="pun">{</span><span class="pln">
                    println</span><span class="pun">!(</span><span class="str">"Worker {id} disconnected; shutting down."</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="typ">Worker</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            id</span><span class="pun">,</span><span class="pln">
            thread</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">thread</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;">
	[الشيفرة 24: الخروج صراحةً من الحلقة عندما تعيد <code>recv</code> خطأ]
</p>

<p>
	لرؤية عمل هذه الشيفرة: سنعدل <code>main</code> لتقبل فقط طلبين قبل أن تُغلق الخادم برشاقة كما تظهر الشيفرة 25.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3018_22" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let listener </span><span class="pun">=</span><span class="pln"> </span><span class="typ">TcpListener</span><span class="pun">::</span><span class="pln">bind</span><span class="pun">(</span><span class="str">"127.0.0.1:7878"</span><span class="pun">).</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">
    let pool </span><span class="pun">=</span><span class="pln"> </span><span class="typ">ThreadPool</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="lit">4</span><span class="pun">);</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> stream in listener</span><span class="pun">.</span><span class="pln">incoming</span><span class="pun">().</span><span class="pln">take</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">
        let stream </span><span class="pun">=</span><span class="pln"> stream</span><span class="pun">.</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">

        pool</span><span class="pun">.</span><span class="pln">execute</span><span class="pun">(||</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            handle_connection</span><span class="pun">(</span><span class="pln">stream</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">"Shutting down."</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 25: إغلاق الخادم بعد خدمة طلبين عن طريق الخروج من الحلقة]
</p>

<p>
	لا نريد أن يتوقف <a href="https://academy.hsoub.com/devops/servers/%D8%AF%D9%84%D9%8A%D9%84-%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%AE%D8%A7%D8%AF%D9%85-%D9%88%D9%8A%D8%A8-%D9%85%D8%AD%D9%84%D9%8A-%D8%AE%D8%B7%D9%88%D8%A9-%D8%A8%D8%AE%D8%B7%D9%88%D8%A9-r422/" rel="">خادم حقيقي</a> بعد خدمة طلبين فقط، وتبين هذه الشيفرة أن الإغلاق الرشيق وتحرير الذاكرة يعملان بصورةٍ نظامية.
</p>

<p>
	تُعرّف دالة <code>take</code> في سمة <code>Iterator</code> وتحدد التكرار إلى أول عنصرين بالحد الأقصى. سيخرج <code>ThreadPool</code> خارج النطاق في نهاية <code>main</code> وستُطبَّق سمة <code>drop</code>.
</p>

<p>
	شغّل الخادم باستخدام <code>cargo run</code> وأرسل ثلاثة طلبات. سيعطي الطلب الثالث خطأ وسترى الخرج في الطرفية على النحو التالي:
</p>

<pre class="ipsCode">$ cargo run
   Compiling hello v0.1.0 (file:///projects/hello)
    Finished dev [unoptimized + debuginfo] target(s) in 1.0s
     Running `target/debug/hello`
Worker 0 got a job; executing.
Shutting down.
Shutting down worker 0
Worker 3 got a job; executing.
Worker 1 disconnected; shutting down.
Worker 2 disconnected; shutting down.
Worker 3 disconnected; shutting down.
Worker 0 disconnected; shutting down.
Shutting down worker 1
Shutting down worker 2
Shutting down worker 3
</pre>

<p>
	يمكن أن ترى ترتيبًا مختلفًا للخيوط الفعالة والرسائل المطبوعة. تعمل الشيفرة وفقًا لهذه الرسائل كما يلي: أخذ العاملان 0 و 3 الطلبين الأولين وتوقف الخادم عن قبول الاتصالات بعد ثاني اتصال، وبدأ تنفيذ <code>Drop</code> في العمل على <code>ThreadPool</code> قبل أخذ العامل 3 وظيفته. يفصل إسقاط <code>sender</code> كل العمال ويخبرهم أن يُغلقوا، ويطبع كل عامل رسالةً عندما يُغلقوا ويستدعي مجمع الخيط <code>join</code> لانتظار كل خيط عامل لينتهي.
</p>

<p>
	لاحظ ميّزة مهمة في هذا التنفيذ، إذ قام <code>ThreadPool</code> بإسقاط <code>sender</code> وجرّبنا ضم العامل 0 قبل أن يستقبل أي عامل خطأ. لم يتلق العامل 0 أي خطأ من <code>recv</code> بعد، لذا تنتظر كتلة الخيط الأساسية أن ينتهي العامل 0. في تلك الأثناء استقبل العامل 3 وظيفة ثم استقبلت كل الخيوط خطأ. ينتظر الخيط الأساسي باقي العمال لينتهوا عندما ينتهي العامل 0. وبحلول هذه النقطة يخرج كل عامل من حلقته ويتوقف.
</p>

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

<p>
	هذه هي الشيفرة الكاملة بمثابة مرجع.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3018_25" style=""><span class="pln">use hello</span><span class="pun">::</span><span class="typ">ThreadPool</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">
use std</span><span class="pun">::</span><span class="pln">io</span><span class="pun">::</span><span class="pln">prelude</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">TcpListener</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">TcpStream</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">thread</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">time</span><span class="pun">::</span><span class="typ">Duration</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 listener </span><span class="pun">=</span><span class="pln"> </span><span class="typ">TcpListener</span><span class="pun">::</span><span class="pln">bind</span><span class="pun">(</span><span class="str">"127.0.0.1:7878"</span><span class="pun">).</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">
    let pool </span><span class="pun">=</span><span class="pln"> </span><span class="typ">ThreadPool</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="lit">4</span><span class="pun">);</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> stream in listener</span><span class="pun">.</span><span class="pln">incoming</span><span class="pun">().</span><span class="pln">take</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">
        let stream </span><span class="pun">=</span><span class="pln"> stream</span><span class="pun">.</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">

        pool</span><span class="pun">.</span><span class="pln">execute</span><span class="pun">(||</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            handle_connection</span><span class="pun">(</span><span class="pln">stream</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">"Shutting down."</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

fn handle_connection</span><span class="pun">(</span><span class="pln">mut stream</span><span class="pun">:</span><span class="pln"> </span><span class="typ">TcpStream</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let mut buffer </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">0</span><span class="pun">;</span><span class="pln"> </span><span class="lit">1024</span><span class="pun">];</span><span class="pln">
    stream</span><span class="pun">.</span><span class="pln">read</span><span class="pun">(&amp;</span><span class="pln">mut buffer</span><span class="pun">).</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">

    let get </span><span class="pun">=</span><span class="pln"> b</span><span class="str">"GET / HTTP/1.1\r\n"</span><span class="pun">;</span><span class="pln">
    let sleep </span><span class="pun">=</span><span class="pln"> b</span><span class="str">"GET /sleep HTTP/1.1\r\n"</span><span class="pun">;</span><span class="pln">

    let </span><span class="pun">(</span><span class="pln">status_line</span><span class="pun">,</span><span class="pln"> filename</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"> buffer</span><span class="pun">.</span><span class="pln">starts_with</span><span class="pun">(</span><span class="pln">get</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="pun">(</span><span class="str">"HTTP/1.1 200 OK"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"hello.html"</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"> buffer</span><span class="pun">.</span><span class="pln">starts_with</span><span class="pun">(</span><span class="pln">sleep</span><span class="pun">)</span><span class="pln"> </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">5</span><span class="pun">));</span><span class="pln">
        </span><span class="pun">(</span><span class="str">"HTTP/1.1 200 OK"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"hello.html"</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="pun">(</span><span class="str">"HTTP/1.1 404 NOT FOUND"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"404.html"</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">filename</span><span class="pun">).</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">

    let response </span><span class="pun">=</span><span class="pln"> format</span><span class="pun">!(</span><span class="pln">
        </span><span class="str">"{}\r\nContent-Length: {}\r\n\r\n{}"</span><span class="pun">,</span><span class="pln">
        status_line</span><span class="pun">,</span><span class="pln">
        contents</span><span class="pun">.</span><span class="pln">len</span><span class="pun">(),</span><span class="pln">
        contents
    </span><span class="pun">);</span><span class="pln">

    stream</span><span class="pun">.</span><span class="pln">write_all</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">as_bytes</span><span class="pun">()).</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">
    stream</span><span class="pun">.</span><span class="pln">flush</span><span class="pun">().</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3018_27" style=""><span class="pln">use std</span><span class="pun">::{</span><span class="pln">
    sync</span><span class="pun">::{</span><span class="pln">mpsc</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Arc</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Mutex</span><span class="pun">},</span><span class="pln">
    thread</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">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    workers</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">Worker</span><span class="pun">&gt;,</span><span class="pln">
    sender</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Option</span><span class="pun">&lt;</span><span class="pln">mpsc</span><span class="pun">::</span><span class="typ">Sender</span><span class="pun">&lt;</span><span class="typ">Job</span><span class="pun">&gt;&gt;,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

type </span><span class="typ">Job</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">FnOnce</span><span class="pun">()</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="typ">Send</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'</span><span class="kwd">static</span><span class="pun">&gt;;</span><span class="pln">

impl </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">/// Create a new ThreadPool.</span><span class="pln">
    </span><span class="com">///</span><span class="pln">
    </span><span class="com">/// The size is the number of threads in the pool.</span><span class="pln">
    </span><span class="com">///</span><span class="pln">
    </span><span class="com">/// # Panics</span><span class="pln">
    </span><span class="com">///</span><span class="pln">
    </span><span class="com">/// The `new` function will panic if the size is zero.</span><span class="pln">
    pub fn </span><span class="kwd">new</span><span class="pun">(</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> usize</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        assert</span><span class="pun">!(</span><span class="pln">size </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">

        let </span><span class="pun">(</span><span class="pln">sender</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> mpsc</span><span class="pun">::</span><span class="pln">channel</span><span class="pun">();</span><span class="pln">

        let receiver </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Arc</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Mutex</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="pln">receiver</span><span class="pun">));</span><span class="pln">

        let mut workers </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">::</span><span class="pln">with_capacity</span><span class="pun">(</span><span class="pln">size</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">for</span><span class="pln"> id in </span><span class="lit">0.</span><span class="pun">.</span><span class="pln">size </span><span class="pun">{</span><span class="pln">
            workers</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="typ">Worker</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="pln">id</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Arc</span><span class="pun">::</span><span class="pln">clone</span><span class="pun">(&amp;</span><span class="pln">receiver</span><span class="pun">)));</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            workers</span><span class="pun">,</span><span class="pln">
            sender</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">sender</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 fn execute</span><span class="pun">&lt;</span><span class="pln">F</span><span class="pun">&gt;(&amp;</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="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">+</span><span class="pln"> </span><span class="typ">Send</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'</span><span class="kwd">static</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        let job </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="pln">f</span><span class="pun">);</span><span class="pln">

        self</span><span class="pun">.</span><span class="pln">sender</span><span class="pun">.</span><span class="pln">as_ref</span><span class="pun">().</span><span class="pln">unwrap</span><span class="pun">().</span><span class="pln">send</span><span class="pun">(</span><span class="pln">job</span><span class="pun">).</span><span class="pln">unwrap</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="typ">Drop</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn drop</span><span class="pun">(&amp;</span><span class="pln">mut self</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        drop</span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">sender</span><span class="pun">.</span><span class="pln">take</span><span class="pun">());</span><span class="pln">

        </span><span class="kwd">for</span><span class="pln"> worker in </span><span class="pun">&amp;</span><span class="pln">mut self</span><span class="pun">.</span><span class="pln">workers </span><span class="pun">{</span><span class="pln">
            println</span><span class="pun">!(</span><span class="str">"Shutting down worker {}"</span><span class="pun">,</span><span class="pln"> worker</span><span class="pun">.</span><span class="pln">id</span><span class="pun">);</span><span class="pln">

            </span><span class="kwd">if</span><span class="pln"> let </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">thread</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> worker</span><span class="pun">.</span><span class="pln">thread</span><span class="pun">.</span><span class="pln">take</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                thread</span><span class="pun">.</span><span class="pln">join</span><span class="pun">().</span><span class="pln">unwrap</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="kwd">struct</span><span class="pln"> </span><span class="typ">Worker</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    id</span><span class="pun">:</span><span class="pln"> usize</span><span class="pun">,</span><span class="pln">
    thread</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Option</span><span class="pun">&lt;</span><span class="pln">thread</span><span class="pun">::</span><span class="typ">JoinHandle</span><span class="pun">&lt;()&gt;&gt;,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

impl </span><span class="typ">Worker</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">id</span><span class="pun">:</span><span class="pln"> usize</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Arc</span><span class="pun">&lt;</span><span class="typ">Mutex</span><span class="pun">&lt;</span><span class="pln">mpsc</span><span class="pun">::</span><span class="typ">Receiver</span><span class="pun">&lt;</span><span class="typ">Job</span><span class="pun">&gt;&gt;&gt;)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Worker</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let thread </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"> loop </span><span class="pun">{</span><span class="pln">
            let message </span><span class="pun">=</span><span class="pln"> receiver</span><span class="pun">.</span><span class="pln">lock</span><span class="pun">().</span><span class="pln">unwrap</span><span class="pun">().</span><span class="pln">recv</span><span class="pun">();</span><span class="pln">

            match message </span><span class="pun">{</span><span class="pln">
                </span><span class="typ">Ok</span><span class="pun">(</span><span class="pln">job</span><span class="pun">)</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">"Worker {id} got a job; executing."</span><span class="pun">);</span><span class="pln">

                    job</span><span class="pun">();</span><span class="pln">
                </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="pun">{</span><span class="pln">
                    println</span><span class="pun">!(</span><span class="str">"Worker {id} disconnected; shutting down."</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="typ">Worker</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            id</span><span class="pun">,</span><span class="pln">
            thread</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">thread</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>
	يمكننا إجراء المزيد إذا أردنا تحسين المشروع، وإليك بعض الأفكار:
</p>

<ul>
	<li>
		أضِف المزيد من التوثيق إلى <code>ThreadPool</code> وتوابعه العامة.
	</li>
	<li>
		أضِف بعض الاختبارات لوظيفة المكتبة.
	</li>
	<li>
		غيّر الاستدعاءات من <code>unwrap</code> إلى معالجة خطأ أكثر متانة.
	</li>
	<li>
		استخدم <code>ThreadPool</code> لتنفيذ أعمال غير خدمة طلبات الويب.
	</li>
	<li>
		ابحث عن وحدة مجمع خيط مصرفة على creats.io ونفذ خادم ويب باستخدام الوحدة المصرفة، ثم قارن واجهة برمجة التطبيقات <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> والمتانة بينها وبين مجمع الخيط الذي نفذناه.
	</li>
</ul>

<h2 id="-2">
	خاتمة
</h2>

<p>
	عظيم جدًا! فقد وصلنا إلى نهاية سلسلة <a href="https://academy.hsoub.com/search/?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&amp;updated_after=any&amp;sortby=newest" rel="">البرمجة بلغة رست</a> . نريد أن نشكرك لانضمامك إلينا في هذه الجولة في رست. أنت الآن جاهز لتنفيذ مشاريع رست ومساعدة الآخرين في مشاريعهم. تذكر أنه هناك مجتمع مرحب من مستخدمي رست الذين يحبون المساعدة في أي صعوبة يمكن أن تواجهها في استعمالك رست.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch20-03-graceful-shutdown-and-cleanup.html" rel="external nofollow">Final Project: Building a Multithreaded Web Server</a> من كتاب <a href="https://doc.rust-lang.org/stable/book/title-page.html/" rel="external nofollow">The Rust Programming Language</a>.
</p>

<h2 id="-3">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%A8%D9%86%D8%A7%D8%A1-%D8%AE%D8%A7%D8%AF%D9%85-%D9%88%D9%8A%D8%A8-%D9%85%D8%AA%D8%B9%D8%AF%D8%AF-%D9%85%D9%87%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%AB%D8%A7%D9%86%D9%8A-r2151/" rel="">بناء خادم ويب متعدد مهام المعالجة بلغة رست - الجزء الثاني</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D8%A7%D9%84%D8%AD%D8%A7%D9%84%D8%A9-%D8%A7%D9%84%D9%85%D8%B4%D8%AA%D8%B1%D9%83%D8%A9-shared-state-concurrency-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D9%88%D8%AA%D9%88%D8%B3%D9%8A%D8%B9-%D8%A7%D9%84%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D9%85%D8%B9-send-%D9%88-sync-r2089/" rel="">تزامن الحالة المشتركة Shared-State Concurrency في لغة رست وتوسيع التزامن مع Send و Sync</a>
	</li>
	<li>
		<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="">مقدمة إلى الخيوط Threads في جافا</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2157</guid><pubDate>Thu, 02 Nov 2023 13:00:00 +0000</pubDate></item><item><title>&#x628;&#x646;&#x627;&#x621; &#x62E;&#x627;&#x62F;&#x645; &#x648;&#x64A;&#x628; &#x645;&#x62A;&#x639;&#x62F;&#x62F; &#x645;&#x647;&#x627;&#x645; &#x627;&#x644;&#x645;&#x639;&#x627;&#x644;&#x62C;&#x629; &#x628;&#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A; - &#x627;&#x644;&#x62C;&#x632;&#x621; &#x627;&#x644;&#x62B;&#x627;&#x646;&#x64A;</title><link>https://academy.hsoub.com/programming/rust/%D8%A8%D9%86%D8%A7%D8%A1-%D8%AE%D8%A7%D8%AF%D9%85-%D9%88%D9%8A%D8%A8-%D9%85%D8%AA%D8%B9%D8%AF%D8%AF-%D9%85%D9%87%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%AB%D8%A7%D9%86%D9%8A-r2151/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_10/--------Rust-(-).png.636dc1881a19ad8fce8f60187eabbacf.png" /></p>
<p>
	سنكمل في هذا المقال ما تحدثنا عنه في المقال السابق <a href="https://academy.hsoub.com/programming/rust/%D8%A8%D9%86%D8%A7%D8%A1-%D8%AE%D8%A7%D8%AF%D9%85-%D9%88%D9%8A%D8%A8-%D9%85%D8%AA%D8%B9%D8%AF%D8%AF-%D9%85%D9%87%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%A3%D9%88%D9%84-r2150/" rel="">الجزء الأول</a> عملية بناء خادم ويب متعدد مهام المعالجة، فإذا لم تكن قد قرأت المقال السابق، فاقرأه قبل قراءة هذا المقال.
</p>

<h2>
	تحويل خادم ويب ذو خيط وحيد إلى خادم متعدد المهام
</h2>

<p>
	يعالج الآن <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r574/" rel="">خادم الويب</a> كل طلب بدوره، يعني أنه لن يعالج اتصال ثاني حتى ينتهي من معالجة الطلب الأول. سيصبح التنفيذ التسلسلي أقل كفاءةً كلما زادت الطلبات على الخادم؛ فإذا استقبل الخادم طلبًا يتطلب وقتًا طويلًا لمعالجته ستنتظر الطلبات التالية وقتًا أطول حتى ينتهي الطلب الطويل حتى لو كانت الطلبات التالية تُنفذ بسرعة. يجب حل هذه المشكلة ولكن أولًا لنلاحظها أثناء العمل.
</p>

<h2>
	محاكاة طلب بطيء في تنفيذ الخادم الحالي
</h2>

<p>
	لنلاحظ كيف يؤثر طلب بطيء المعالجة على الطلبات الأخرى المقدمة إلى تنفيذ الخادم الحالي. تنفذ الشيفرة 10 طلب معالجة إلى ‎/sleep بمحاكاة استجابة بطيئة التي تسبب سكون الخادم لخمس ثوانٍ قبل الاستجابة.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3274_8" style=""><span class="pln">use std</span><span class="pun">::{</span><span class="pln">
    fs</span><span class="pun">,</span><span class="pln">
    io</span><span class="pun">::{</span><span class="pln">prelude</span><span class="pun">::*,</span><span class="pln"> </span><span class="typ">BufReader</span><span class="pun">},</span><span class="pln">
    net</span><span class="pun">::{</span><span class="typ">TcpListener</span><span class="pun">,</span><span class="pln"> </span><span class="typ">TcpStream</span><span class="pun">},</span><span class="pln">
    thread</span><span class="pun">,</span><span class="pln">
    time</span><span class="pun">::</span><span class="typ">Duration</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">

fn handle_connection</span><span class="pun">(</span><span class="pln">mut stream</span><span class="pun">:</span><span class="pln"> </span><span class="typ">TcpStream</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 </span><span class="pun">(</span><span class="pln">status_line</span><span class="pun">,</span><span class="pln"> filename</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> match </span><span class="pun">&amp;</span><span class="pln">request_line</span><span class="pun">[..]</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="str">"GET / HTTP/1.1"</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">"HTTP/1.1 200 OK"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"hello.html"</span><span class="pun">),</span><span class="pln">
        </span><span class="str">"GET /sleep HTTP/1.1"</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </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">5</span><span class="pun">));</span><span class="pln">
            </span><span class="pun">(</span><span class="str">"HTTP/1.1 200 OK"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"hello.html"</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">(</span><span class="str">"HTTP/1.1 404 NOT FOUND"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"404.html"</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;">
	[الشيفرة 10: محاكاة استجابة بطيئة عن طريق سكون الخادم لخمس ثوان]
</p>

<p>
	بدلنا من <code>if</code> إلى <code>match</code> إذ لدينا ثلاث حالات. يجب أن نطابق صراحةً مع قطعة من <code>request_line</code> لمطابقة النمط مع قيم السلسلة النصية المجردة. لا تُسند <code>match</code> ولا تُحصل تلقائيًا كما تفعل توابع المساواة.
</p>

<p>
	تكون الذراع الأولى هي نفس كتلة <code>if</code> من الشيفرة 9، وتطابق الذراع الثانية الطلب إلى ‎/sleep ويسكن الخادم لخمس ثوان عندما يُستقبل الطلب قبل تصيير صفحة HTML الناجحة، والذراع الثالثة هي نفس كتلة <code>else</code> من الشيفرة 9.
</p>

<p>
	يمكن ملاحظة أن الخادم بدائي، لكن تعالج المكاتب الحقيقية طلبات متعددة بطريقة مختصرة أكثر.
</p>

<p>
	شغل الخادم باستخدام <code>cargo run</code>، ثم افتح نافذتي متصفح واحدة من أجل "/http://127.0.0.1:7878" وأُخرى من أجل "http://127.0.0.1:7878/sleep". إذا أدخلت ‎/ URI عدة مرات كما سابقًا سترى أنه يستجيب بسرعة، لكن إذا أدخلت ‎/sleep ومن ثم حمّلت "/" سترى أن "/" ينتظر حتى يسكن <code>sleep</code> خمس ثوان كاملة قبل أن يُحمّل.
</p>

<p>
	هناك تقنيات متعددة لتفادي التراكم خلف طلب بطيء، والطريقة التي سنتبعها هي مجمع خيط thread pool.
</p>

<h2>
	تحسن الإنتاجية باستخدام مجمع خيط
</h2>

<p>
	<a href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%88%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D8%AA%D9%88%D8%A7%D8%B2%D9%8A-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1485/" rel="">مجمع خيط thread pool</a> هو مجموعة من الخيوط المُنشأة التي تنتظر معالجة مهمة. عندما يستقبل البرنامج مهمةً، يُعيّن واحد من الخيوط في المجمع لأداء المهمة ومعالجتها وتبقى باقي الخيوط في المجمع متاحةً لمعالجة أي مهمة تأتي أثناء معالجة الخيط الأول للمهمة، وعندما ينتهي الخيط من معالجة المهمة يعود إلى مجمع الخيوط الخاملة جاهزًا لمعالجة أي مهمة جديدة. يسمح مجمع خيط معالجة الاتصالات بصورةٍ متزامنة ويزيد إنتاجية الخادم.
</p>

<p>
	سنحدد عدد الخيوط في المجمع برقم صغير لحمايتنا من <a href="https://academy.hsoub.com/devops/networking/%D8%A7%D9%84%D9%87%D8%AC%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D9%85%D9%86%D9%8A%D8%A9-security-attacks-%D9%81%D9%8A-%D8%A7%D9%84%D8%B4%D8%A8%D9%83%D8%A7%D8%AA-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8%D9%8A%D8%A9-r540/" rel="">هجوم حجب الخدمة Denial of Service ‎</a> -أو اختصارًا DoS. إذا طلبنا من البرنامج إنشاء خيط لكل طلب قادم، يمكن لشخص إنشاء 10 مليون طلب مستهلكًا كل الموارد المتاحة وموقفًا معالجة الطلبات نهائيًا.
</p>

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

<p>
	هذه إحدى طرق زيادة إنتاجية خادم ويب، ويمكن استكشاف طرق أُخرى مثل نموذج اشتقاق/جمع fork/join أو نموذج الدخل والخرج للخيط الواحد غير المتزامن single-threaded async I/O أو نموذج الدخل والخرج للخيوط المتعددة غير المتزامن multi-threaded async I/O model. يمكنك قراءة وتنفيذ هذه الحلول إذا كنت مهتمًا بهذا الموضوع وكل هذه الخيارات ممكنة مع لغة برمجية ذات مستوى منخفض مثل رست.
</p>

<p>
	قبل البدء بتنفيذ مجمع خيط، لنتحدث كيف يجب أن يكون استخدام المجمع. عند بداية تصميم الشيفرة، تساعدك كتابة واجهة المستخدم في التصميم. اكتب واجهة برمجة التطبيق <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> للشيفرة بهيكلية تشبه طريقة استدعائها، ثم نفذ الوظيفة داخل الهيكل بدلًا من تنفيذ الوظيفة ثم بناء واجهة برمجة التطبيق العامة.
</p>

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

<h2>
	إنشاء خيط لكل طلب
</h2>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3274_10" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let listener </span><span class="pun">=</span><span class="pln"> </span><span class="typ">TcpListener</span><span class="pun">::</span><span class="pln">bind</span><span class="pun">(</span><span class="str">"127.0.0.1:7878"</span><span class="pun">).</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> stream in listener</span><span class="pun">.</span><span class="pln">incoming</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let stream </span><span class="pun">=</span><span class="pln"> stream</span><span class="pun">.</span><span class="pln">unwrap</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"> </span><span class="pun">{</span><span class="pln">
            handle_connection</span><span class="pun">(</span><span class="pln">stream</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;">
	[الشيفرة 11: إنشاء خيط جديد لكل مجرى]
</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%AE%D9%8A%D9%88%D8%B7-threads-%D9%84%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%B1%D8%B3%D8%AA-%D8%A8%D8%B5%D9%88%D8%B1%D8%A9-%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D8%A2%D9%86%D9%8A%D9%8B%D8%A7-r2043/" rel="">استخدام الخيوط Threads لتنفيذ شيفرات رست بصورة متزامنة آنيًا</a>، يُنشئ <code>thread::spawn</code> خيطًا جديدًا وينفذ الشيفرة في المُغلف في الخيط الجديد. إذا نفذت الشيفرة وحملت "‎/sleep" في المتصفح ومن ثم "/" في نافذتي متصفح أُخريين ستلاحظ أن الطلبات إلى "/" لا تنتظر "‎/sleep‎" لينتهي ولكن كما ذكرنا سابقًا سيطغى هذا على النظام لأننا ننشئ خيوطًا دون حد.
</p>

<h2>
	إنشاء عدد محدد من الخيوط
</h2>

<p>
	نريد من مجمع الخيط أن يعمل بطريقة مشابهة ومألوفة حتى لا يحتاج التبديل من الخيوط لمجمع خيط أي تعديلات كبيرة للشيفرة التي تستخدمها واجهة برمجة التطبيق. تظهر الشيفرة 12 واجهة افتراضية لهيكل <code>ThreadPool</code> الذي نريد استخدامه بدلًا عن <code>thread::spawn</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3274_12" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let listener </span><span class="pun">=</span><span class="pln"> </span><span class="typ">TcpListener</span><span class="pun">::</span><span class="pln">bind</span><span class="pun">(</span><span class="str">"127.0.0.1:7878"</span><span class="pun">).</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">
    let pool </span><span class="pun">=</span><span class="pln"> </span><span class="typ">ThreadPool</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="lit">4</span><span class="pun">);</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> stream in listener</span><span class="pun">.</span><span class="pln">incoming</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let stream </span><span class="pun">=</span><span class="pln"> stream</span><span class="pun">.</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">

        pool</span><span class="pun">.</span><span class="pln">execute</span><span class="pun">(||</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            handle_connection</span><span class="pun">(</span><span class="pln">stream</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;">
	[الشيفرة 12: واجهة <code>ThreadPool</code> المثالية]
</p>

<p>
	استخدمنا <code>ThreadPool::new</code> لإنشاء مجمع خيط جديد بعدد خيوط يمكن تعديله وفي حالتنا أربعة. لدى <code>pool.execute</code> واجهة مماثلة للدالة <code>thread:spawn</code> في حلقة <code>for</code> إذ تأخذ مغلفًا يجب أن ينفذه المجمع لكل مجرى. نحتاج لتنفيذ <code>pool.execute</code> أن تأخذ مغلفًا وتعطيه لخيط في المجمع لينفذه. لن تُصرّف هذه الشيفرة ولكن سنجربها كي يدلنا المصرف عن كيفية إصلاحها.
</p>

<h2>
	إنشاء مجمع خيط باستخدام التطوير المقاد بالمصرف
</h2>

<p>
	أجرِ التغييرات في الشيفرة 12 على الملف src/main.rs واستخدم أخطاء المصرّف من <code>cargo check</code> لقيادة التطوير. هذه أول خطأ نحصل عليه:
</p>

<pre class="ipsCode">$ cargo check
    Checking hello v0.1.0 (file:///projects/hello)
error[E0433]: failed to resolve: use of undeclared type `ThreadPool`
  --&gt; src/main.rs:11:16
   |
11 |     let pool = ThreadPool::new(4);
   |                ^^^^^^^^^^ use of undeclared type `ThreadPool`

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

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

<p>
	أنشئ src/lib.rs الذي يحتوي التالي، وهو أبسط تعريف لهيكل <code>ThreadPool</code> يمكن الحصول عليه.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3274_14" style=""><span class="pln">pub </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">ThreadPool</span><span class="pun">;</span></pre>

<p>
	ثم عدل ملف main.rs لجلب <code>ThreadPool</code> من المكتبة إلى النطاق بإضافة الشيفرة التالية في مقدمة الملف src/main.rs.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3274_16" style=""><span class="pln">use hello</span><span class="pun">::</span><span class="typ">ThreadPool</span><span class="pun">;</span></pre>

<p>
	لن تعمل هذه الشيفرة ولكن لننظر مجددًا إلى الخطأ التالي الذي نريد معالجته:
</p>

<pre class="ipsCode">$ cargo check
    Checking hello v0.1.0 (file:///projects/hello)
error[E0599]: no function or associated item named `new` found for struct `ThreadPool` in the current scope
  --&gt; src/main.rs:12:28
   |
12 |     let pool = ThreadPool::new(4);
   |                            ^^^ function or associated item not found in `ThreadPool`

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

<p>
	يشير هذا الخطأ أننا نحتاج إلى انتاج دالة مرتبطة اسمها <code>new</code> من أجل <code>ThreadPool</code>. يمكننا معرفة أن <code>new</code> تحتاج معامل يقبل 4 مثل وسيط ويجب أن يعيد نسخة <code>ThreadPool</code>. لننفذ أبسط دالة <code>new</code> التي تحتوي هذه الصفات characteristics.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3274_19" style=""><span class="pln">pub </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">ThreadPool</span><span class="pun">;</span><span class="pln">

impl </span><span class="typ">ThreadPool</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">size</span><span class="pun">:</span><span class="pln"> usize</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">ThreadPool</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	اخترنا نوع <code>usize</code> للمعامل <code>size</code> لأننا نعرف أن العدد السالب للطلبات غير منطقي ونعرف أيضًا أننا سنستخدم 4 ليمثّل عدد العناصر في مجموعة الخيوط وهذا هو عمل نوع <code>usize</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="">أنواع البيانات Data Types في لغة رست</a>.
</p>

<p>
	لنتحقق من الشيفرة مجددًا:
</p>

<pre class="ipsCode">$ cargo check
    Checking hello v0.1.0 (file:///projects/hello)
error[E0599]: no method named `execute` found for struct `ThreadPool` in the current scope
  --&gt; src/main.rs:17:14
   |
17 |         pool.execute(|| {
   |              ^^^^^^^ method not found in `ThreadPool`

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

<p>
	يحدث الخطأ الآن لأنه ليس لدينا تابع <code>execute</code> على <code>ThreadPool</code>. تذكر من قسم "إنشاء عدد محدد من الخيوط" أننا قررنا أن مجمع الخيط يجب أن يكون له واجهة تشبه <code>thread::spawn</code>، وقررنا أيضّأ أننا سننفذ التابع <code>execute</code> ليأخذ المغلف المعُطى له ويعطيه لخيط خامل في المجمع لينفذه.
</p>

<p>
	سنعرّف تابع <code>execute</code> على <code>ThreadPool</code> ليأخذ المغلف مثل معامل. تذكر من القسم "نقل القيم خارج المغلف وسمات <code>Fn</code>" في المقال <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 في لغة رست</a> أننا بإمكاننا أخذ المغلفات مثل معاملات باستخدام ثلاث سمات هي <code>Fn</code> أو <code>FnMut</code> أو <code>FnOnce</code>. يجب أن نحدد أي نوع مغلف نريد استخدامه هنا، نحن نعرف أننا سنفعل شيئًا يشابه تنفيذ المكتبة القياسية لدالة<code>thread::spawn</code>، لذلك دعنا نرى ما هي القيود الموجودة لبصمة <code>thread::spawn</code> على معاملاتها. تظهر التوثيقات التالي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_3274_21" style=""><span class="pln">pub fn spawn</span><span class="pun">&lt;</span><span class="pln">F</span><span class="pun">,</span><span class="pln"> T</span><span class="pun">&gt;(</span><span class="kwd">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"> JoinHandle</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln">
    where
        </span><span class="kwd">F</span><span class="pun">:</span><span class="pln"> 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">
        </span><span class="kwd">F</span><span class="pun">:</span><span class="pln"> Send </span><span class="pun">+</span><span class="pln"> 'static</span><span class="pun">,</span><span class="pln">
        </span><span class="kwd">T</span><span class="pun">:</span><span class="pln"> Send </span><span class="pun">+</span><span class="pln"> 'static</span><span class="pun">,</span></pre>

<p>
	المعامل <code>F</code> هو الذي يهمنا، والمعامل <code>T</code> متعلق بالقيمة المُعادة ولسنا مهتمين بها. يمكننا أن نرى أن <code>spawn</code> تستخدم <code>FnOnce</code> مثل قيد سمة على <code>F</code>، وهذا ما نريده أيضًأ لأننا نريد تمرير الوسيط الذي نأخذه في <code>execute</code> إلى <code>spawn</code>. يمكننا التأكد أيضًا أن <code>FnOnce</code> هي السمة المُراد استخدامها لأن خيط تنفيذ الطلب سينفِّذ فقط طلب المغلف مرةً واحدة، والذي يطابق <code>Once</code> في <code>FnOnce</code>.
</p>

<p>
	لدى معامل نوع <code>F</code> أيضًا قيد سمة <code>Send</code> وقيد دورة حياة <code>static'</code> المفيدان في حالتنا؛ فنحن بحاجة <code>Send</code> لنقل المغلف من خيط لآخر، و <code>static'</code> لأننا لا نعرف الوقت اللازم ليُنفذ الخيط. لننشئ تابع <code>execute</code> على <code>ThreadPool</code> التي تأخذ معامل معمم للنوع <code>F</code> مع هذه القيود.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3274_23" style=""><span class="pln">impl </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
    pub fn execute</span><span class="pun">&lt;</span><span class="pln">F</span><span class="pun">&gt;(&amp;</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="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">+</span><span class="pln"> </span><span class="typ">Send</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'</span><span class="kwd">static</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>()</code> بعد <code>FnOnce</code> لأن <code>FnOnce</code> تمثل مغلفًا لا يأخذ معاملات ويعيد نوع الوحدة <code>()</code>. يمكن إهمال النوع المُعاد من البصمة كما في تعريفات الدالة، ولكن حتى لو لم يوجد أي معاملات نحن بحاجة الأقواس.
</p>

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

<pre class="ipsCode">$ cargo check
    Checking hello v0.1.0 (file:///projects/hello)
    Finished dev [unoptimized + debuginfo] target(s) in 0.24s
</pre>

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

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

<h2>
	التحقق من صحة عدد الخيوط في new
</h2>

<p>
	لن نغيّر شيئًا للمعاملين <code>new</code> و <code>parameter</code>. لننفذ متن الدوال بالسلوك الذي نريده، ولنبدأ بالدالة <code>new</code>. اخترنا سابقًا نوع غير مؤشر للمعامل <code>size</code> لأن مجمع بعدد خيوط سلبي هو غير منطقي، ولكن مجمع بعدد خيوط صفر ليس منطقيًا أيضًا ولكن <code>unsize</code> صالح. سنضيف الشيفرة التي تتحقق من أن <code>size</code> أكبر من الصفر قبل إعادة نسخة من <code>ThreadPool</code> وجعل البرنامج يهلع إذا حصل على قيمة صفر باستخدام ماكرو <code>assert!‎</code> كما في الشيفرة 13.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3274_26" style=""><span class="pln">impl </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">/// Create a new ThreadPool.</span><span class="pln">
    </span><span class="com">///</span><span class="pln">
    </span><span class="com">/// The size is the number of threads in the pool.</span><span class="pln">
    </span><span class="com">///</span><span class="pln">
    </span><span class="com">/// # Panics</span><span class="pln">
    </span><span class="com">///</span><span class="pln">
    </span><span class="com">/// The `new` function will panic if the size is zero.</span><span class="pln">
    pub fn </span><span class="kwd">new</span><span class="pun">(</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> usize</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        assert</span><span class="pun">!(</span><span class="pln">size </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">

        </span><span class="typ">ThreadPool</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;">
	[الشيفرة 13: تنفيذ <code>Threadpool:new</code> ليهلع إذا كان <code>size</code> صفر]
</p>

<p>
	أضفنا بعض التوثيق إلى <code>ThreadPool</code> باستخدام تعليقات doc. لاحظ أننا اتبعنا خطوات التوثيق الجيدة بإضافة قسم يستدعي الحالات التي يمكن للدالة أن تهلع فيها كما تحدثنا في الفصل 14. جرب تنفيذ <code>cargo run --open</code> واضغط على هيكل <code>ThreadPool</code> لرؤية كيف تبدو المستندات المُنشأة للدالة <code>new</code>.
</p>

<p>
	يمكننا تغيير <code>new</code> إلى <code>build</code> بدلًا من إضافة ماكرو <code>assert!‎</code>، ونعيد <code>Result</code> كما فعلنا في <code>Config::build</code> في مشروع الدخل والخرج في الشيفرة 9 في المقال <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>، لكننا قررنا في حالتنا أن إنشاء مجمع خيط بدون أي خيوط هو خطأ لا يمكن استرداده. إذا كنت طموحًا جرب كتابة دالة اسمها <code>build</code> مع البصمة التالية لمقارنته مع الدالة <code>new</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3274_28" style=""><span class="pln">pub fn build</span><span class="pun">(</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> usize</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">ThreadPool</span><span class="pun">,</span><span class="pln"> </span><span class="typ">PoolCreationError</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span></pre>

<h2>
	إنشاء مساحة لتخزين الخيوط
</h2>

<p>
	لدينا الآن طريقة لمعرفة أنه لدينا عدد صالح من الخيوط لتخزينها في المجمع، إذ يمكننا إنشاء هذه الخيوط وتخزينها في هيكل <code>ThreadPool</code> قبل إرجاعها إلى الهيكل، ولكن كيف نخزن الخيوط؟ لنلاحظ بصمة <code>thread::spawn</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3274_30" style=""><span class="pln">pub fn spawn</span><span class="pun">&lt;</span><span class="pln">F</span><span class="pun">,</span><span class="pln"> T</span><span class="pun">&gt;(</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"> </span><span class="typ">JoinHandle</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln">
    </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">
        F</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Send</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'</span><span class="kwd">static</span><span class="pun">,</span><span class="pln">
        T</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Send</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'static,</span></pre>

<p>
	يُعاد <code>JoinHandle&lt;T&gt;‎</code> من الدالة <code>spawn</code>، إذ تمثّل <code>T</code> النوع الذي يعيده المغلف. لنستعمل <code>JoinHandle</code> أيضًا لنرى ما سيحدث، إذ سيعالج المغلف الذي نمرره إلى مجمع الخيط الاتصال ولا يعيد أي شيء لذا <code>T</code> ستكون نوع وحدة <code>()</code>.
</p>

<p>
	ستُصرّف الشيفرة في الشيفرة 14 ولكن لا تُنشئ أي خيوط. غيّرنا تعريف <code>ThreadPool</code> لتحتوي شعاعًا من نسخة <code>thread::JoinHandle&lt;()&gt;‎</code> وهيأنا الشعاع بسعة <code>size</code> وضبطنا حلقة <code>for</code> التي تعيد بعض الشيفرة لإنشاء الخيوط وتعيد نسخة <code>ThreadPool</code> تحتويهم.
</p>

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

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

pub </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    threads</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="pln">thread</span><span class="pun">::</span><span class="typ">JoinHandle</span><span class="pun">&lt;()&gt;&gt;,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

impl </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
    pub fn </span><span class="kwd">new</span><span class="pun">(</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> usize</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        assert</span><span class="pun">!(</span><span class="pln">size </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">

        let mut threads </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">::</span><span class="pln">with_capacity</span><span class="pun">(</span><span class="pln">size</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">for</span><span class="pln"> _ in </span><span class="lit">0.</span><span class="pun">.</span><span class="pln">size </span><span class="pun">{</span><span class="pln">
            </span><span class="com">// create some threads and store them in the vector</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> threads </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;">
	[الشيفرة 14: إنشاء شعاع للهيكل <code>ThreadPool</code> الذي يحتوي الخيوط]
</p>

<p>
	جلبنا <code>std::thread</code> إلى النطاق في وحدة المكتبة المصرفة لأننا نستخدم <code>Thread::JoinHandle</code> بمثابة نوع العنصر في الشعاع في <code>ThreadPool</code>. تُنشئ <code>ThreadPool</code> شعاعًا جديدًا يحتوي عناصر <code>size</code> عندما يُستقبل حجم صالح.
</p>

<p>
	تعمل الدالة <code>with_capacity</code> نفس مهام <code>Vec::new</code> ولكن بفرق مهم هو أنها تحجز مسبقًا المساحة في الشعاع لأننا نريد تخزين عناصر <code>size</code> في الشعاع. إجراء هذا الحجز مسبقًا هو أكثر كفاءة من استخدام <code>Vec::new</code> الذي يغير حجمه كلما اُضيفت عناصر.
</p>

<p>
	عندما تنفذ <code>cargo check</code> مجدّدًا ينبغي أن تنجح.
</p>

<h2>
	هيكل عامل Worker Struct مسؤول عن ارسال شيفرة من مجمع الخيط إلى خيط
</h2>

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

<p>
	سننفذ هذا السلوك عن طريق إضافة هيكلية بيانات جديدة بين <code>ThreadPool</code> والخيوط التي ستدير هذه السلوك الجديد، وسندعو هيكل البيانات هذا "العامل Worker" وهو مصطلح عام في تنفيذات مجمّع الخيوط. يأخذ العامل الشيفرة التي بحاجة لتنفيذ وينفّذها في خيط العامل. فكر كيف يعمل الناس في مطبخ المطعم، إذ ينتظر العاملون طلبات الزبائن ويكونوا مسؤولين عن أخذ هذه الطلبات وتنفيذها.
</p>

<p>
	بدلًا من تخزين شعاع نسخة <code>JoinHandle&lt;()&gt;‎</code> في مجمع الخيط، نخزن نسخًا من هيكل <code>Worker</code>. يخزن كل <code>Worker</code> نسخة <code>JoinHandle&lt;()&gt;‎</code> واحدة، ثم ننفذ تابع على <code>Worker</code> الذي يأخذ مغلف شيفرة لينفذه ويرسله إلى خيط يعمل حاليًا لينفذه. سنعطي كل عامل رقمًا معرّفًا <code>id</code> للتمييز بين العمال المختلفين في المجمع عند التسجيل أو تنقيح الأخطاء.
</p>

<p>
	هكذا ستكون العملية الجديدة عند إنشاء <code>ThreadPool</code>. سننفذ الشيفرة التي ترسل المغلف إلى الخيط بعد إعداد <code>Worker</code> بهذه الطريقة:
</p>

<ol>
	<li>
		عرّف هيكل <code>Worker</code> الذي يحتوي <code>id</code> و <code>JoinHandle&lt;()&gt;‎</code>.
	</li>
	<li>
		عدّل <code>ThreadPool</code> لتحتوي شعاع من نسخ <code>Worker</code>.
	</li>
	<li>
		عرّف دالة <code>Worker::new</code> التي تأخذ رقم <code>id</code> وتعيد نسخة <code>Worker</code> التي تحتوي <code>id</code> وخيط مُنشأ بمغلف فارغ.
	</li>
	<li>
		استخدم عداد حلقة <code>for</code> لإنشاء <code>id</code> وإنشاء <code>Worker</code> جديد مع ذلك الرقم <code>id</code> وخرن العامل في الشعاع.
	</li>
</ol>

<p>
	إذا كنت جاهزًا للتحدي، جرّب تنفيذ هذه التغييرات بنفسك قبل النظر إلى الشيفرة في الشيفرة 15.
</p>

<p>
	جاهز؟ يوجد في الشيفرة 15 إحدى طرق عمل التعديلات السابقة.
</p>

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

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

pub </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    workers</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">Worker</span><span class="pun">&gt;,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

impl </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
    pub fn </span><span class="kwd">new</span><span class="pun">(</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> usize</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        assert</span><span class="pun">!(</span><span class="pln">size </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">

        let mut workers </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">::</span><span class="pln">with_capacity</span><span class="pun">(</span><span class="pln">size</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">for</span><span class="pln"> id in </span><span class="lit">0.</span><span class="pun">.</span><span class="pln">size </span><span class="pun">{</span><span class="pln">
            workers</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="typ">Worker</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="pln">id</span><span class="pun">));</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> workers </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="kwd">struct</span><span class="pln"> </span><span class="typ">Worker</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    id</span><span class="pun">:</span><span class="pln"> usize</span><span class="pun">,</span><span class="pln">
    thread</span><span class="pun">:</span><span class="pln"> thread</span><span class="pun">::</span><span class="typ">JoinHandle</span><span class="pun">&lt;()&gt;,</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

impl </span><span class="typ">Worker</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">id</span><span class="pun">:</span><span class="pln"> usize</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Worker</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let thread </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"> </span><span class="pun">{});</span><span class="pln">

        </span><span class="typ">Worker</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">,</span><span class="pln"> thread </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>ThreadPool</code> بحيث تحتوي نسخة <code>Worker</code> بدلًا من احتواء الخيط مباشرةً]
</p>

<p>
	عدّلنا اسم حقل <code>ThreadPool</code> من <code>threads</code> إلى <code>workers</code> لأنه يحتوي نسخ <code>Worker</code> بدلًا من نسخ <code>JoinHandle&lt;()&gt;‎</code>. استخدمنا العداد في حلقة <code>for</code> مثل وسيط لدالة <code>Worker::new</code> وخزّنا كل <code>Worker</code> جديد في شعاع اسمه <code>workers</code>.
</p>

<p>
	لا تحتاج الشيفرة الخارجية (كما في الخادم في src/main.rs) أن تعرف تفاصيل التنفيذ بما يتعلق باستخدام هيكل <code>Worker</code> داخل <code>ThreadPool</code>، لذا نجعل كل من هيكل <code>Worker</code> ودالة <code>new</code> خاصين private. تستخدم الدالة <code>Worker::new</code> المعرّف <code>id</code> المُعطى وتخزن نسخة <code>JoinHandle&lt;()&gt;‎</code> المُنشأة عن طريق إنشاء خيط جديد باستخدام مغلف فارغ.
</p>

<p>
	<strong>ملاحظة:</strong> سيهلع <code>thread::spawn</code> إذا كان نظام التشغيل لا يستطيع إنشاء خيط بسبب عدم توفر موارد كافية، وسيؤدي هذا إلى هلع كامل الخادم حتى لو كان إنشاء بعض الخيوط ممكنًا. للتبسيط يمكن قبول هذا السلوك ولكن في تنفيذ مجمع خيط مُنتج ينبغي استخدام <code>std::thread::Builder</code> ودالة <code>spawn</code> الخاصة به التي تعيد <code>Result</code>.
</p>

<p>
	تُصرّف هذه الشيفرة وتخزن عددًا من نسخ <code>Worker</code> الذي حددناه مثل وسيط إلى <code>ThreadPool::new</code>. لكننا لم نعالج المغلف الذي نحصل عليه في <code>execute</code>. لنتعرف على كيفية عمل ذلك تاليًا.
</p>

<h2>
	إرسال طلبات إلى الخيوط عن طريق القنوات
</h2>

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

<p>
	نريد تشغيل هياكل <code>Worker</code> التي أنشأناها للبحث عن شيفرة من الرتل في <code>ThreadPool</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-%D9%85%D9%8A%D8%B2%D8%A9-%D8%AA%D9%85%D8%B1%D9%8A%D8%B1-%D8%A7%D9%84%D8%B1%D8%B3%D8%A7%D8%A6%D9%84-message-passing-%D9%84%D9%86%D9%82%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2045/" rel="">استخدام ميزة تمرير الرسائل Message Passing لنقل البيانات بين الخيوط Threads في لغة رست</a> -والتي تُعد طريقة بسيطة للتواصل بين خيطين- طريقةً ممتازةً لحالتنا، إذ سنستعمل قناةً لتعمل مثل رتل للوظائف، وترسل <code>execute</code> وظيفة من <code>ThreadPool</code> إلى نسخة <code>Worker</code> التي ترسل بدورها الوظيفة إلى خيطها. ستكون الخطة على النحو التالي:
</p>

<ol>
	<li>
		يُنشئ <code>ThreadPool</code> قناة ويحتفظ بالمرسل.
	</li>
	<li>
		يحتفظ كل <code>Worker</code> بالمستقبل.
	</li>
	<li>
		ننشئ هيكل <code>Job</code> جديد يحتفظ بالمغلف الذي نريد إرساله عبر القناة.
	</li>
	<li>
		يرسل تابع <code>execute</code> الوظيفة المراد تنفيذها عبر المرسل.
	</li>
	<li>
		سيتكرر مرور <code>Worker</code> على المستقبل وينفذ المغلف لأي وظيفة يستقبلها في الخيط.
	</li>
</ol>

<p>
	لنحاول إنشاء قناة في <code>ThreadPool::new</code> والاحتفاظ بالمرسل في نسخة <code>ThreadPool</code> كما في الشيفرة 16. لا يحتوي هيكل <code>Job</code> أي شيء الآن، لكنه سيكون نوع العنصر المُرسل عبر القناة.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3274_36" style=""><span class="pln">use std</span><span class="pun">::{</span><span class="pln">sync</span><span class="pun">::</span><span class="pln">mpsc</span><span class="pun">,</span><span class="pln"> thread</span><span class="pun">};</span><span class="pln">

pub </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    workers</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">Worker</span><span class="pun">&gt;,</span><span class="pln">
    sender</span><span class="pun">:</span><span class="pln"> mpsc</span><span class="pun">::</span><span class="typ">Sender</span><span class="pun">&lt;</span><span class="typ">Job</span><span class="pun">&gt;,</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">Job</span><span class="pun">;</span><span class="pln">

impl </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
    pub fn </span><span class="kwd">new</span><span class="pun">(</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> usize</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        assert</span><span class="pun">!(</span><span class="pln">size </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">

        let </span><span class="pun">(</span><span class="pln">sender</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> mpsc</span><span class="pun">::</span><span class="pln">channel</span><span class="pun">();</span><span class="pln">

        let mut workers </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">::</span><span class="pln">with_capacity</span><span class="pun">(</span><span class="pln">size</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">for</span><span class="pln"> id in </span><span class="lit">0.</span><span class="pun">.</span><span class="pln">size </span><span class="pun">{</span><span class="pln">
            workers</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="typ">Worker</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="pln">id</span><span class="pun">));</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> workers</span><span class="pun">,</span><span class="pln"> sender </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;">
	[الشيفرة 16: تعديل <code>ThreadPool</code> لتخزين مرسل القناة التي ترسل نسخ <code>Job</code>]
</p>

<p>
	أنشأنا القناة الجديدة في <code>ThreadPool:new</code> وجعلنا المجمع يحتفظ بالمرسل. ستصرّف هذه الشيفرة بنجاح.
</p>

<p>
	لنجرب تمرير مستقبل القناة إلى كل عامل عندما ينشئ مجمع الخيط القناة. نعرف أننا نريد استخدام المستقبل في الخيط الذي أنشأه العامل، لذا سنشير إلى معامل <code>receiver</code> في المغلف بمرجع reference. لن تُصرّف الشيفرة 17.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3274_38" style=""><span class="pln">impl </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
    pub fn </span><span class="kwd">new</span><span class="pun">(</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> usize</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        assert</span><span class="pun">!(</span><span class="pln">size </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">

        let </span><span class="pun">(</span><span class="pln">sender</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> mpsc</span><span class="pun">::</span><span class="pln">channel</span><span class="pun">();</span><span class="pln">

        let mut workers </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">::</span><span class="pln">with_capacity</span><span class="pun">(</span><span class="pln">size</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">for</span><span class="pln"> id in </span><span class="lit">0.</span><span class="pun">.</span><span class="pln">size </span><span class="pun">{</span><span class="pln">
            workers</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="typ">Worker</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="pln">id</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">));</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> workers</span><span class="pun">,</span><span class="pln"> sender </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="com">// --snip--</span><span class="pln">

impl </span><span class="typ">Worker</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">id</span><span class="pun">:</span><span class="pln"> usize</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">:</span><span class="pln"> mpsc</span><span class="pun">::</span><span class="typ">Receiver</span><span class="pun">&lt;</span><span class="typ">Job</span><span class="pun">&gt;)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Worker</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let thread </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"> </span><span class="pun">{</span><span class="pln">
            receiver</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">});</span><span class="pln">

        </span><span class="typ">Worker</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">,</span><span class="pln"> thread </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;">
	[الشيفرة 17: تمرير المستقبل إلى العمال workers]
</p>

<p>
	أجرينا بعض التغييرات الصغيرة والمباشرة، إذ مررنا المستقبل إلى <code>Worker::new</code> واستخدمناه داخل المغلف.
</p>

<p>
	عندما نتحقق من الشيفرة سنحصل على هذا الخطأ:
</p>

<pre class="ipsCode">$ cargo check
    Checking hello v0.1.0 (file:///projects/hello)
error[E0382]: use of moved value: `receiver`
  --&gt; src/lib.rs:26:42
   |
21 |         let (sender, receiver) = mpsc::channel();
   |                      -------- move occurs because `receiver` has type `std::sync::mpsc::Receiver&lt;Job&gt;`, which does not implement the `Copy` trait
...
26 |             workers.push(Worker::new(id, receiver));
   |                                          ^^^^^^^^ value moved here, in previous iteration of loop

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

<p>
	تحاول الشيفرة تمرير <code>receiver</code> لنسخ متعددة من <code>Worker</code> ولكن هذا لن يعمل كما تتذكر سابقًا من المقال <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D9%8A%D8%B2%D8%A9-%D8%AA%D9%85%D8%B1%D9%8A%D8%B1-%D8%A7%D9%84%D8%B1%D8%B3%D8%A7%D8%A6%D9%84-message-passing-%D9%84%D9%86%D9%82%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2045/" rel="">استخدام ميزة تمرير الرسائل Message Passing لنقل البيانات بين الخيوط Threads في لغة رست</a>، إذ أن تنفيذ القناة المقدم من رست هو مُنتجين producer متعددين ومستهلك consumer واحد، وهذا يعني أنه لا يمكن نسخ الطرف المستهلك من القناة لإصلاح هذا الخطأ ولا نريد أيضًا إرسال رسائل متعددة لمستهلكين متعددين، بل نحتاج قائمة رسائل واحدة مع عمال متعددين لكي تعالج كل رسالة مرةً واحدةً فقط. إضافةً إلى ذلك، يتطلب أخذ وظيفة من رتل القناة تغيير <code>receiver</code>، لذا تحتاج الخيوط طريقةً آمنةً لتشارك وتعدل <code>receiver</code>، وإلا نحصل على حالات سباق (كما تحدثنا في الفصل السابق المشار إليه).
</p>

<p>
	تذكر المؤشرات الذكية الآمنة للخيوط التي تحدثنا عنها سابقًا في المقال <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D8%A7%D9%84%D8%AD%D8%A7%D9%84%D8%A9-%D8%A7%D9%84%D9%85%D8%B4%D8%AA%D8%B1%D9%83%D8%A9-shared-state-concurrency-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D9%88%D8%AA%D9%88%D8%B3%D9%8A%D8%B9-%D8%A7%D9%84%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D9%85%D8%B9-send-%D9%88-sync-r2089/" rel="">تزامن الحالة المشتركة Shared-State Concurrency في لغة رست وتوسيع التزامن مع Send و Sync</a>؛ فنحن بحاجة لاستخدام <code>Arc&lt;Mutex&lt;T&gt;&gt;‎</code> لمشاركة الملكية لعدد من الخيوط والسماح للخيوط بتغيير القيمة. يسمح نوع <code>Arc</code> لعدد من العمال من مُلك المستقبل وتضمن <code>Mutex</code> حصول عامل واحد على الوظيفة من المستقبل. تظهر الشيفرة 18 التغييرات التي يجب عملها.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3274_40" style=""><span class="pln">use std</span><span class="pun">::{</span><span class="pln">
    sync</span><span class="pun">::{</span><span class="pln">mpsc</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Arc</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Mutex</span><span class="pun">},</span><span class="pln">
    thread</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">

impl </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
    pub fn </span><span class="kwd">new</span><span class="pun">(</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> usize</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        assert</span><span class="pun">!(</span><span class="pln">size </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">

        let </span><span class="pun">(</span><span class="pln">sender</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> mpsc</span><span class="pun">::</span><span class="pln">channel</span><span class="pun">();</span><span class="pln">

        let receiver </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Arc</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Mutex</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="pln">receiver</span><span class="pun">));</span><span class="pln">

        let mut workers </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">::</span><span class="pln">with_capacity</span><span class="pun">(</span><span class="pln">size</span><span class="pun">);</span><span class="pln">

        </span><span class="kwd">for</span><span class="pln"> id in </span><span class="lit">0.</span><span class="pun">.</span><span class="pln">size </span><span class="pun">{</span><span class="pln">
            workers</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="typ">Worker</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="pln">id</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Arc</span><span class="pun">::</span><span class="pln">clone</span><span class="pun">(&amp;</span><span class="pln">receiver</span><span class="pun">)));</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> workers</span><span class="pun">,</span><span class="pln"> sender </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="com">// --snip--</span><span class="pln">

impl </span><span class="typ">Worker</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">id</span><span class="pun">:</span><span class="pln"> usize</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Arc</span><span class="pun">&lt;</span><span class="typ">Mutex</span><span class="pun">&lt;</span><span class="pln">mpsc</span><span class="pun">::</span><span class="typ">Receiver</span><span class="pun">&lt;</span><span class="typ">Job</span><span class="pun">&gt;&gt;&gt;)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Worker</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;">
	[الشيفرة 18: مشاركة المستقبل بين العمال باستخدام <code>Arc</code> و <code>Mutex</code>]
</p>

<p>
	نضع المستقبل في<code>ThreadPool::new</code> في <code>Arc</code> و <code>Mutex</code>، وننسخ <code>Arc</code> لكل عامل لتزيد عدّ المرجع ليستطيع العمال مشاركة ملكية المستقبل.
</p>

<p>
	تُصرّف الشيفرة بنجاح مع هذه التغييرات، لقد اقتربنا من تحقيق هدفنا.
</p>

<h2>
	تنفيذ تابع التنفيذ execute
</h2>

<p>
	لننفذ أخيرًا تابع <code>execute</code> على <code>ThreadPool</code>، إذ سغيّر أيضًا <code>Job</code> من هيكل إلى نوع اسم بديل لكائن السمة الذي يحتوي نوع المغلف الذي يستقبله <code>execute</code>. كما تحدثنا في قسم "إنشاء مرادفات للنوع بواسطة أسماء النوع البديلة" في المقال <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D9%88%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2148/" rel="">الأنواع والدوال المتقدمة في لغة رست</a>، يسمح لنا نوع الاسم البديل بتقصير الأنواع الطويلة لسهولة الاستخدام كما في الشفرة 19.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3274_42" style=""><span class="com">// --snip--</span><span class="pln">

type </span><span class="typ">Job</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">FnOnce</span><span class="pun">()</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="typ">Send</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'</span><span class="kwd">static</span><span class="pun">&gt;;</span><span class="pln">

impl </span><span class="typ">ThreadPool</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">

    pub fn execute</span><span class="pun">&lt;</span><span class="pln">F</span><span class="pun">&gt;(&amp;</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="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">+</span><span class="pln"> </span><span class="typ">Send</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'</span><span class="kwd">static</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        let job </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="pln">f</span><span class="pun">);</span><span class="pln">

        self</span><span class="pun">.</span><span class="pln">sender</span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="pln">job</span><span class="pun">).</span><span class="pln">unwrap</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">// --snip--</span></pre>

<p style="text-align: center;">
	[الشيفرة 19: إنشاء نوع اسم بديل <code>Job</code> لـ <code>Box</code> يحتوي كل مغلف وارسال العمل عبر القناة]
</p>

<p>
	بعد إنشاء نسخة <code>Job</code> جديدة باستخدام المغلف نحصل على <code>execute</code> ونرسل الوظيفة عبر الطرف المرسل للقناة. نستدعي <code>unwrap</code> على <code>send</code> في حال فشل الإرسال؛ إذ يمكن حصول ذلك إذا أوقفنا كل الخيوط من التنفيذ، وهذا يعني توقُف الطرف المستقبل عن استقبال أي رسائل جديدة. لا يمكننا الآن إيقاف الخيوط من التنفيذ، إذ تستمر خيوطنا بالتنفيذ طالما المجمع موجود. سبب استخدام <code>unwrap</code> هو أننا نعرف أن حالة الفشل هذه لن تحصل ولكن المصرّف لا يعرف ذلك.
</p>

<p>
	لم ننتهي كليًا بعد، فالمغلف المُمرر إلى <code>thread::spawn</code> يسند الطرف المستقبل من القناة فقط، لكن نريد بدلًا من ذلك أن يتكرر المغلف للأبد ويسأل الطرف المستقبل من القناة عن وظيفة وينفذ الوظيفة عندما يحصل عليها. دعنا نجري التغييرات الموضحة في الشيفرة 20 للدالة <code>Worker::new</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3274_44" style=""><span class="com">// --snip--</span><span class="pln">

impl </span><span class="typ">Worker</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">id</span><span class="pun">:</span><span class="pln"> usize</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Arc</span><span class="pun">&lt;</span><span class="typ">Mutex</span><span class="pun">&lt;</span><span class="pln">mpsc</span><span class="pun">::</span><span class="typ">Receiver</span><span class="pun">&lt;</span><span class="typ">Job</span><span class="pun">&gt;&gt;&gt;)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Worker</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let thread </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"> loop </span><span class="pun">{</span><span class="pln">
            let job </span><span class="pun">=</span><span class="pln"> receiver</span><span class="pun">.</span><span class="pln">lock</span><span class="pun">().</span><span class="pln">unwrap</span><span class="pun">().</span><span class="pln">recv</span><span class="pun">().</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">

            println</span><span class="pun">!(</span><span class="str">"Worker {id} got a job; executing."</span><span class="pun">);</span><span class="pln">

            job</span><span class="pun">();</span><span class="pln">
        </span><span class="pun">});</span><span class="pln">

        </span><span class="typ">Worker</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">,</span><span class="pln"> thread </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>lock</code> على <code>receiver</code> للحصول على mutex، ونستدعي <code>unwrap</code> ليهلع على أي خطأ. قد يفشل الحصول على قفل إذا كان mutex في حالة مسمومة poisoned، والتي تحصل إذا هلع أحد الخيوط عند احتفاظه بالقفل بدلًا من ترك القفل، وسيكون استدعاء <code>unwrap</code> في هذه الحالة هو العمل الأفضل. غيّر <code>unwrap</code> إلى <code>expect</code> على راحتك لتظهر رسالة خطأ ذات معنى.
</p>

<p>
	إذا حصلنا على القفل على mutex، نستدعي <code>recv</code> لاستقبال <code>Job</code> من القناة. يتخطى استدعاء <code>unwrap</code> الأخير أي أخطاء أيضًا والتي ربما قد تحصل إذا اُغلق، على نحوٍ مشابه لكيفية إعادة <code>Err</code> من قِبل تابع <code>send</code> إذا أُغلق المستقبل.
</p>

<p>
	إذا لم توجد أي وظيفة في استدعاء كتل <code>recv</code>، سينتظر الخيط حتى تتوفر وظيفة. يضمن <code>Mutex&lt;T&gt;‎</code> أن يكون هناك خيط <code>Worker</code> واحد يطلب وظيفة.
</p>

<p>
	يعمل مجمع الخيط الآن، جرب <code>cargo run</code> وأرسل بعض الطلبات.
</p>

<pre class="ipsCode">$ cargo run
   Compiling hello v0.1.0 (file:///projects/hello)
warning: field is never read: `workers`
 --&gt; src/lib.rs:7:5
  |
7 |     workers: Vec&lt;Worker&gt;,
  |     ^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: field is never read: `id`
  --&gt; src/lib.rs:48:5
   |
48 |     id: usize,
   |     ^^^^^^^^^

warning: field is never read: `thread`
  --&gt; src/lib.rs:49:5
   |
49 |     thread: thread::JoinHandle&lt;()&gt;,
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: `hello` (lib) generated 3 warnings
    Finished dev [unoptimized + debuginfo] target(s) in 1.40s
     Running `target/debug/hello`
Worker 0 got a job; executing.
Worker 2 got a job; executing.
Worker 1 got a job; executing.
Worker 3 got a job; executing.
Worker 0 got a job; executing.
Worker 2 got a job; executing.
Worker 1 got a job; executing.
Worker 3 got a job; executing.
Worker 0 got a job; executing.
Worker 2 got a job; executing.
</pre>

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

<p>
	<strong>ملاحظة:</strong> إذا فتحنا ‎/sleep في نوافذ متعددة في المتصفح بنفس الوقت، ستُحمل واحدةٌ تلو الأُخرى بفواصل زمنية مدتها 5 ثواني لأن بعض المتصفحات تنفذ النسخ المتعددة لنفس الطلب بالترتيب لأسباب التخزين المؤقت. ليس الخادم هو سبب هذا التقصير.
</p>

<p>
	بعد أن تعلمنا عن حلقة <code>while let</code> في المقال <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A3%D9%86%D9%85%D8%A7%D8%B7-patterns-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D8%A7%D8%AA%D9%87%D8%A7-%D9%88%D9%82%D8%A7%D8%A8%D9%84%D9%8A%D8%AA%D9%87%D8%A7-%D9%84%D9%84%D8%AF%D8%AD%D8%B6-refutability-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2109/" rel="">الأنماط Patterns واستخداماتها وقابليتها للدحض Refutability في لغة رست</a>، ربما تتساءل لماذا لم نكتب شيفرة الخيط العامل كما في الشيفرة 21.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3274_46" style=""><span class="com">// --snip--</span><span class="pln">

impl </span><span class="typ">Worker</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">id</span><span class="pun">:</span><span class="pln"> usize</span><span class="pun">,</span><span class="pln"> receiver</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Arc</span><span class="pun">&lt;</span><span class="typ">Mutex</span><span class="pun">&lt;</span><span class="pln">mpsc</span><span class="pun">::</span><span class="typ">Receiver</span><span class="pun">&lt;</span><span class="typ">Job</span><span class="pun">&gt;&gt;&gt;)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Worker</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let thread </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"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">while</span><span class="pln"> let </span><span class="typ">Ok</span><span class="pun">(</span><span class="pln">job</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> receiver</span><span class="pun">.</span><span class="pln">lock</span><span class="pun">().</span><span class="pln">unwrap</span><span class="pun">().</span><span class="pln">recv</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">"Worker {id} got a job; executing."</span><span class="pun">);</span><span class="pln">

                job</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">Worker</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">,</span><span class="pln"> thread </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;">
	[الشيفرة 21: طريقة تنفيذ مختلفة لدالة <code>Worker::new</code> باستخدام <code>while let</code>]
</p>

<p>
	تُصرّف الشيفرة وتُنفذ ولكن لا تعطي نتيجة عمل الخيوط المرغوبة، إذ يسبب الطلب البطيء انتظار باقي الطلبات لتُعالج، والسبب بسيطٌ إلى حد ما؛ فليس لدى هيكل <code>Mutex</code> دالة <code>unlock</code> عامة لأن ملكية القفل مبينةٌ على دورة حياة <code>MutexGuard&lt;T&gt;‎</code> داخل <code>LockResult&lt;MutexGuard&lt;T&gt;&gt;‎</code> التي يعيدها التابع <code>lock</code>. يطبق متحقق الاستعارة قاعدة أن المورد المحمي بهيكل <code>Mutex</code> لا يمكن الوصول له إلا إذا احتفظنا بالقفل وقت التصريف، ولكن بهذا التنفيذ يمكن أن يبقى القفل مُحتفظًا به أكثر من اللازم إذا لم نكن منتبهين إلى دورة حياة <code>MutexGuard&lt;T&gt;‎</code>.
</p>

<p>
	تعمل الشيفرة في الشيفرة 20 التي تستخدم <code>let job = receiver.lock().unwrap().recv().unwrap();‎</code> إذ تُسقط أي قيمة مؤقتة مُستخدمة في التعبير على الطرف اليمين من إشارة المساواة "=" مع <code>let</code>عندما تنتهي تعليمة <code>let</code>، ولكن لا تُسقط <code>while let</code> (وأيضًا <code>if let</code> و <code>match</code>) القيم المؤقتة حتى نهاية الكتلة المرتبطة بها. يبقى القفل مُحتفظًا به حتى نهاية فترة استدعاء <code>job()‎</code> يعني أن العمال الباقين لا يمكن أن يستقبلوا وظائف.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch20-00-final-project-a-web-server.html" rel="external nofollow">Final Project: Building a Multithreaded Web Server</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%A8%D9%86%D8%A7%D8%A1-%D8%AE%D8%A7%D8%AF%D9%85-%D9%88%D9%8A%D8%A8-%D9%85%D8%AA%D8%B9%D8%AF%D8%AF-%D9%85%D9%87%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%AB%D8%A7%D9%84%D8%AB-r2157/" rel="">بناء خادم ويب متعدد مهام المعالجة بلغة رست - الجزء الثالث</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%A8%D9%86%D8%A7%D8%A1-%D8%AE%D8%A7%D8%AF%D9%85-%D9%88%D9%8A%D8%A8-%D9%85%D8%AA%D8%B9%D8%AF%D8%AF-%D9%85%D9%87%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%A3%D9%88%D9%84-r2150/" rel="">بناء خادم ويب متعدد مهام المعالجة بلغة رست - الجزء الأول</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%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%AE%D8%A7%D8%AF%D9%85-r783/" rel="">مدخل إلى برمجة مواقع الويب من طرف الخادم</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/web/%D8%A3%D9%81%D8%B6%D9%84-5-%D8%AE%D9%88%D8%A7%D8%AF%D9%85-%D9%88%D9%8A%D8%A8-%D9%85%D9%81%D8%AA%D9%88%D8%AD%D8%A9-%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1-r370/" rel="">أفضل 5 خوادم ويب مفتوحة المصدر</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2151</guid><pubDate>Thu, 26 Oct 2023 13:05:00 +0000</pubDate></item><item><title>&#x628;&#x646;&#x627;&#x621; &#x62E;&#x627;&#x62F;&#x645; &#x648;&#x64A;&#x628; &#x645;&#x62A;&#x639;&#x62F;&#x62F; &#x645;&#x647;&#x627;&#x645; &#x627;&#x644;&#x645;&#x639;&#x627;&#x644;&#x62C;&#x629; &#x628;&#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A; - &#x627;&#x644;&#x62C;&#x632;&#x621; &#x627;&#x644;&#x623;&#x648;&#x644;</title><link>https://academy.hsoub.com/programming/rust/%D8%A8%D9%86%D8%A7%D8%A1-%D8%AE%D8%A7%D8%AF%D9%85-%D9%88%D9%8A%D8%A8-%D9%85%D8%AA%D8%B9%D8%AF%D8%AF-%D9%85%D9%87%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%A3%D9%88%D9%84-r2150/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_10/--------Rust.png.1f97450d86f0a2d33a56ada04436c410.png" /></p>
<p>
	بعد رحلة طويلة وصلنا إلى نهاية السلسلة <a href="https://academy.hsoub.com/search/?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&amp;updated_after=any&amp;sortby=newest" rel="">البرمجة بلغة رست</a>. سنبني في هذا القسم مشروعًا لتوضيح بعض المفاهيم التي تحدثنا عنها في المقالات السابقة وتذكر بعض الدروس السابقة.
</p>

<p>
	سنبني خادم ويب يعرض "hello" ويشبه الشكل 1 في متصفح الويب.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="137017" href="https://academy.hsoub.com/uploads/monthly_2023_10/trpl20-01.png.d85f46d31c741d095df27ac41625d290.png" rel=""><img alt="trpl20-01.png" class="ipsImage ipsImage_thumbnailed" data-fileid="137017" data-unique="ifc2pdu9u" src="https://academy.hsoub.com/uploads/monthly_2023_10/trpl20-01.png.d85f46d31c741d095df27ac41625d290.png"> </a>
</p>

<p style="text-align: center;">
	[الشكل1: مشروعنا الأخير المشترك]
</p>

<p>
	هذه هي خطة بناء خادم الويب:
</p>

<ol>
	<li>
		مقدمة عن TCP و HTTP
	</li>
	<li>
		الاستماع إلى اتصالات TCP على المقبس socket
	</li>
	<li>
		تحليل عدد صغير من طلبات HTTP
	</li>
	<li>
		إنشاء استجابة HTTP مناسبة
	</li>
	<li>
		تطوير خرج الخادم بمجمع خيط thread pool
	</li>
</ol>

<p>
	قبل البدء، يجب التنويه على أن هذه الطريقة ليست أفضل طريقة لبناء <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r574/" rel="">خادم ويب</a> باستخدام رست، إذ نشر أعضاء المجتمع وحدات مصرفة جاهزة للتطبيق على <a href="https://crates.io/" rel="external nofollow">creats.io</a>، والتي تقدم خوادم ويب أكثر اكتمالًا وتطبيقات لمجمع خيط أفضل من الذي سنبنيه، ولكن هدفنا من هذا الفصل هو مساعدتك على التعلم وليس اختيار الطريق الأسهل. يمكننا اختيار مستوى التجريد الذي نريد العمل معه لأن رست هي لغة برمجية للأنظمة ويمكن الانتقال لمستوى أدنى مما هو ممكن أو عملي في بعض اللغات الأُخرى، لذلك سنكتب خادم HTTP بسيط ومجمع الخيط يدويًا لنتعلم الأفكار والتقنيات العامة الموجودة في الوحدات المصرفة التي يمكن أن تراها في المستقبل.
</p>

<h2>
	بناء خادم ويب أحادي الخيط
</h2>

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

<p>
	البروتوكولان الأساسيان المعنيان في خوادم الويب هما بروتوكول <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="">نقل النصوص الفائقة Hypertext Transfer Protocol‏</a> -أو اختصارًا HTTP- وبروتوكول <a href="https://academy.hsoub.com/devops/servers/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A8%D8%B1%D9%88%D8%AA%D9%88%D9%83%D9%88%D9%84-tcpip-%D9%88%D8%A8%D8%B9%D8%B6-%D9%85%D9%86-%D8%AE%D8%AF%D9%85%D8%A7%D8%AA%D9%87-r169/" rel="">تحكم النقل Transmission Control Protocol‎</a> -أو اختصارًا TCP، وهما بروتوكولا طلب-استجابة؛ يعني أن العميل يبدأ الطلبات ويسمع الخادم الطلبات ويقدم استجابةً للعميل، ويُعرّف محتوى هذه الطلبات والاستجابات عبر هذه البروتوكولات.
</p>

<p>
	يصف بروتوكول TCP تفاصيل انتقال المعلومات من خادم لآخر ولكن لا يحدد نوع المعلومات. يبني HTTP فوق TCP عن طريق تعريف محتوى الطلبات والاستجابات. يمكن تقنيًا استخدام HTTP مع بروتوكولات أُخرى لكن في معظم الحالات يرسل HTTP البيانات على بروتوكول TCP. سنعمل مع البايتات الخام في طلبات واستجابات TCP و HTTP.
</p>

<h3>
	الاستماع لاتصال TCP
</h3>

<p>
	يجب أن يستمع خادم الويب إلى اتصال TCP لذا سنعمل على هذا الجزء أولًا. تقدم المكتبة القياسية وحدة <code>std::net</code> التي تسمح لنا بذلك. لننشئ مشروعًا جديدًا بالطريقة الاعتيادية:
</p>

<pre class="ipsCode">$ cargo new hello
     Created binary (application) `hello` project
$ cd hello
</pre>

<p>
	الآن اكتب الشيفرة 1 في الملف src/main.rs لنبدأ. ستسمع هذه الشيفرة إلى العنوان المحلي "127.0.0.1:7878" لمجرى TCP stream القادم، وعندما تستقبل مجرى قادم ستطبع <code>Connection established!‎</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1469_8" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">net</span><span class="pun">::</span><span class="typ">TcpListener</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 listener </span><span class="pun">=</span><span class="pln"> </span><span class="typ">TcpListener</span><span class="pun">::</span><span class="pln">bind</span><span class="pun">(</span><span class="str">"127.0.0.1:7878"</span><span class="pun">).</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> stream in listener</span><span class="pun">.</span><span class="pln">incoming</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let stream </span><span class="pun">=</span><span class="pln"> stream</span><span class="pun">.</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">

        println</span><span class="pun">!(</span><span class="str">"Connection established!"</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: الاستماع للمجاري القادمة وطباعة رسالة عند استقبال مجرى]
</p>

<p>
	يمكننا الاستماع لاتصال TCP على هذا العنوان "127.0.0.1:7878" باستخدام <code>TcpListner</code>، إذ يمثّل القسم قبل النقطتين عنوان IP الذي يمثل الحاسوب (هذا العنوان هو نفسه لكل الحواسيب وليس لحاسوب المستخدم حصريًا)، ورقم المنفذ هو 7878. اخترنا هذا المنفذ لسببين: لا يُقبل HTTP على هذا المنفذ لذا لا يتعارض الخادم بأي خدمة ويب ربما تحتاجها على جهازك، و 7878 هي كلمة rust مكتوبة على لوحة أرقام الهاتف.
</p>

<p>
	تعمل دالة <code>bind</code> في هذا الحالة مثل دالة <code>new</code> التي ترجع نسخة <code>TcpListner</code> جديدة. تسمى الدالة <code>bind</code> لأن الاتصال بمنفذ للاستماع إليه هي عملية تُعرف باسم الربط لمنفذ binding to a port. تعيد الدالة <code>bind</code> القيمة <code>Results&lt;T, E&gt;‎</code> التي تشير أنه من الممكن أن يفشل الربط. يتطلب الاتصال بالمنفذ 80 امتيازات المسؤول (يستطيع غير المسؤولين فقط الاستماع في المنافذ الأعلى من 1023)، لذا لا يعمل الارتباط إذا حاولت الاتصال بالمنفذ 80 بدون كونك مسؤول، ولا يعمل الارتباط أيضًا إذا نفذنا نسختين من برنامجنا أي لدينا برنامجين يستمعان لنفس المنفذ. لا يلزمنا أن نتعامل مع هكذا أخطاء لأننا نكتب خادم بسيط لأغراض تعليمية فقط. نستعمل <code>unwrap</code> لإيقاف البرنامج إذا حصلت أي أخطاء.
</p>

<p>
	يعيد التابع <code>incoming</code> على <code>TcpListner</code> مكرّرًا iterator يعطي سلسلةً من المجاري (مجاري نوع <code>TcpStream</code> تحديدًا). يمثل المجرى الواحد اتصالًا مفتوحًا بين العميل والخادم، والاتصال هو الاسم الكامل لعملية الطلب والاستجابة التي يتصل فيها العميل إلى الخادم، وينشئ الخادم استجابةً ويغلق الاتصال. كذلك، سنقرأ من <code>TcpStream</code> لرؤية ماذا أرسل العميل وكتابة استجابتنا إلى المجرى لإرسال البيانات إلى العميل. ستعالج حلقة <code>for</code> عمومًا كل اتصال بدوره وتضيف سلسلة من المجاري لنتعامل معها.
</p>

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

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

<pre class="ipsCode">Running `target/debug/hello`
Connection established!
Connection established!
Connection established!
</pre>

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

<p>
	تذكر أن توقف البرنامج بالضغط على المفتاحين "ctrl-c" عندما تنتهي من تنفيذ نسخة معينة من الشيفرة، بعدها أعد تشغيل البرنامج باستدعاء أمر <code>cargo run</code> بعد إجراء أي تعديل على الشيفرة للتأكد من أنك تنفذ أحدث إصدار منها.
</p>

<h3>
	قراءة الطلب
</h3>

<p>
	دعنا ننفّذ وظيفةً لقراءة الطلب من المتصفح، إذ سنبدأ بدالة جديدة لمعالجة الاتصالات من أجل الفصل بين الحصول على اتصال وإجراء بعض الأعمال بالاتصال. سنقرأ دالة <code>handle_connection</code> البيانات من مجرى TCP وتطبعها لرؤية البيانات التي أُرسلت من المتصفح. غيّر الشيفرة لتصبح مثل الشيفرة 2.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1469_10" style=""><span class="pln">use std</span><span class="pun">::{</span><span class="pln">
    io</span><span class="pun">::{</span><span class="pln">prelude</span><span class="pun">::*,</span><span class="pln"> </span><span class="typ">BufReader</span><span class="pun">},</span><span class="pln">
    net</span><span class="pun">::{</span><span class="typ">TcpListener</span><span class="pun">,</span><span class="pln"> </span><span class="typ">TcpStream</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 listener </span><span class="pun">=</span><span class="pln"> </span><span class="typ">TcpListener</span><span class="pun">::</span><span class="pln">bind</span><span class="pun">(</span><span class="str">"127.0.0.1:7878"</span><span class="pun">).</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> stream in listener</span><span class="pun">.</span><span class="pln">incoming</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let stream </span><span class="pun">=</span><span class="pln"> stream</span><span class="pun">.</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">

        handle_connection</span><span class="pun">(</span><span class="pln">stream</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 handle_connection</span><span class="pun">(</span><span class="pln">mut stream</span><span class="pun">:</span><span class="pln"> </span><span class="typ">TcpStream</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let buf_reader </span><span class="pun">=</span><span class="pln"> </span><span class="typ">BufReader</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(&amp;</span><span class="pln">mut stream</span><span class="pun">);</span><span class="pln">
    let http_request</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"> buf_reader
        </span><span class="pun">.</span><span class="pln">lines</span><span class="pun">()</span><span class="pln">
        </span><span class="pun">.</span><span class="typ">map</span><span class="pun">(|</span><span class="pln">result</span><span class="pun">|</span><span class="pln"> result</span><span class="pun">.</span><span class="pln">unwrap</span><span class="pun">())</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">take_while</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">line</span><span class="pun">.</span><span class="pln">is_empty</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">

    println</span><span class="pun">!(</span><span class="str">"Request: {:#?}"</span><span class="pun">,</span><span class="pln"> http_request</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 2: القراءة من <code>TcpStream</code> وطباعة البيانات]
</p>

<p>
	نضيف <code>std::io::prelude</code> و <code>std::io::BufReader</code> إلى النطاق للحصول على سمات وأنواع تسمح لنا بالقراءة من والكتابة على المجرى. بدلًا من طباعة رسالة تقول أننا اتصلنا، نستدعي الدالة الجديدة <code>handle_connection</code> في حلقة <code>for</code> في الدالة <code>main</code> ونمرّر <code>stream</code> إليها.
</p>

<p>
	أنشأنا نسخة <code>BufReader</code> في دالة <code>handle_connection</code> التي تغلف المرجع المتغيّر إلى <code>stream</code>. يضيف <code>BufReader</code> تخزينًا مؤقتًا عن طريق إدارة الاستدعاءات إلى توابع سمة <code>std::io::Read</code>.
</p>

<p>
	أنشأنا متغيرًا اسمه <code>http_request</code> لجمع أسطر الطلب التي أرسله المتصفح إلى الخادم، ونشير أننا نريد جمع هذه الأسطر في شعاع عن طريق إضافة توصيف نوع <code>Vec&lt;_&gt;‎</code>.
</p>

<p>
	ينفّذ <code>BufReader</code> سمة <code>std::io::BufRead</code> التي تؤمن التابع <code>lines</code>، الذي يعيد مكرّر <code>&lt;Result&lt;String, std::io::Error</code> عن طريق فصل مجرى البيانات أينما ترى بايت سطر جديد. للحصول على كل <code>String</code>، نربط ونزيل تغليف <code>unwarp</code> كل <code>Result</code>. يمكن أن تكون <code>Result</code> خطأ إذا كانت البيانات ليست UTF-8 صالح أو كان هناك مشكلة في القراءة من المجرى. مجددًا، يمكن لبرنامج إنتاجي حل هذه المشكلات بسهولة ولكننا اخترنا إيقاف البرنامج في حالة الخطأ للتبسيط.
</p>

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

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

<pre class="ipsCode">$ cargo run
   Compiling hello v0.1.0 (file:///projects/hello)
    Finished dev [unoptimized + debuginfo] target(s) in 0.42s
     Running `target/debug/hello`
Request: [
    "GET / HTTP/1.1",
    "Host: 127.0.0.1:7878",
    "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:99.0) Gecko/20100101 Firefox/99.0",
    "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
    "Accept-Language: en-US,en;q=0.5",
    "Accept-Encoding: gzip, deflate, br",
    "DNT: 1",
    "Connection: keep-alive",
    "Upgrade-Insecure-Requests: 1",
    "Sec-Fetch-Dest: document",
    "Sec-Fetch-Mode: navigate",
    "Sec-Fetch-Site: none",
    "Sec-Fetch-User: ?1",
    "Cache-Control: max-age=0",
]
</pre>

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

<p>
	لنفصّل بيانات الطلب لفهم ما يطلبه المتصفح من برنامجنا.
</p>

<h3>
	نظرة أقرب على طلب HTTP
</h3>

<p>
	<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-http-%D8%B4%D8%B1%D8%AD-%D8%A7%D9%84%D8%AA%D8%AE%D8%A7%D8%B7%D8%A8-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-r74/" rel="">بروتوكول HTTP</a> أساسه نصي وتكون طلباته على الشكل التالي:
</p>

<pre class="ipsCode">Method Request-URI HTTP-Version CRLF
headers CRLF
message-body
</pre>

<p>
	السطر الأول هو سطر الطلب الذي يحتوي معلومات عما يطلبه العميل، إذ يدل القسم الأول من سطر الطلب على التابع المستخدم مثل <code>GET</code> أو <code>POST</code> الذي يصف كيفية إجراء العميل لهذا الطلب. استخدم عميلنا طلب <code>GET</code>، وهذا يعني أنه يطلب معلومات؛ بينما يشير القسم الثاني "/" من سطر الطلبات إلى معرّف الموارد الموحد Uniform Resource Identifier‎ -أو اختصارًا URI- الذي يطلبه العميل. يشابه URI <a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%B9%D9%86%D9%88%D8%A7%D9%86-url-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9%D9%87-r1435/" rel="">محدد الموارد الموحد Uniform Resource Locator</a> -أو URL اختصارًا- ولكن ليس تمامًا، إذ أن الفرق بينهم ليس مهمًا لهذا المشروع، لكن تستخدم مواصفات HTTP المصطلح URI لذا نستبدل هنا URL بالمصطلح URI.
</p>

<p>
	القسم الأخير هو نسخة HTTP التي يستخدمها العميل، وينتهي الطلب بسلسلة CRLF (تعني CRLF محرف العودة إلى أول السطر والانتقال سطر للأسفل Carriage Return and Line Feed وهما مصطلحان من أيام الآلة الكاتبة). يمكن كتابة سلسلة CRLF مثل <code>‎\r\n</code> إذ أن <code>r\</code> هي محرف العودة إلى أول السطر و <code>n\</code> هو الانتقال سطر للأسفل. تفصل سلسلة CRLF سطر الطلب من باقي بيانات الطلب. نلاحظ عندما تُطبع CRLF نرى بداية سطر جديد بدل <code>‎\r\n</code>.
</p>

<p>
	عند ملاحظة سطر البيانات الذي استقبلناه من تنفيذ برنامجنا حتى الآن نرى أن التابع هو <code>GET</code> وطلب URI هو / والنسخة هي <code>HTTP/1.1</code>.
</p>

<p>
	الأسطر الباقية بدءًا من <code>Host:</code> وبعد هي ترويسات. طلب <code>GET</code> لا يحتوي متن.
</p>

<p>
	حاول عمل طلب من متصفح آخر أو طلب عنوان مختلف مثل 127.0.0.1:7878‎/test لترى كيف تتغير بيانات الطلب.
</p>

<p>
	بعد أن عرفنا ماذا يريد المتصفح لنرسل بعض البيانات.
</p>

<h3>
	كتابة استجابة
</h3>

<p>
	سننفّذ إرسال بيانات مثل استجابة لطلب عميل. لدى الاستجابات التنسيق التالي:
</p>

<pre class="ipsCode">HTTP-Version Status-Code Reason-Phrase CRLF
headers CRLF
message-body
</pre>

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

<p>
	لدينا مثال عن استجابة تستخدم نسخة HTTP 1.1 ولديها <a href="https://academy.hsoub.com/programming/general/%D8%B1%D9%85%D9%88%D8%B2-%D8%A7%D9%84%D8%A5%D8%AC%D8%A7%D8%A8%D8%A9-%D9%81%D9%8A-http-r75/" rel="">رمز حالة 200</a> وعبارة سبب OK بلا ترويسة أو متن.
</p>

<pre class="ipsCode">HTTP/1.1 200 OK\r\n\r\n
</pre>

<p>
	يُعد رمز الحالة 200 استجابة نجاح قياسية والنص هو استجابة نجاح HTTP صغيرة. لنكتب ذلك إلى المجرى مثل استجابة لطلب ناجح. أزل <code>!println</code> التي كانت تطبع طلب البيانات من الدالة <code>handle_connection</code> واستبدلها بالشيفرة 3.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1469_14" style=""><span class="pln">fn handle_connection</span><span class="pun">(</span><span class="pln">mut stream</span><span class="pun">:</span><span class="pln"> </span><span class="typ">TcpStream</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let buf_reader </span><span class="pun">=</span><span class="pln"> </span><span class="typ">BufReader</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(&amp;</span><span class="pln">mut stream</span><span class="pun">);</span><span class="pln">
    let http_request</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"> buf_reader
        </span><span class="pun">.</span><span class="pln">lines</span><span class="pun">()</span><span class="pln">
        </span><span class="pun">.</span><span class="typ">map</span><span class="pun">(|</span><span class="pln">result</span><span class="pun">|</span><span class="pln"> result</span><span class="pun">.</span><span class="pln">unwrap</span><span class="pun">())</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">take_while</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">line</span><span class="pun">.</span><span class="pln">is_empty</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">

    let response </span><span class="pun">=</span><span class="pln"> </span><span class="str">"HTTP/1.1 200 OK\r\n\r\n"</span><span class="pun">;</span><span class="pln">

    stream</span><span class="pun">.</span><span class="pln">write_all</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">as_bytes</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;">
	[الشيفرة 3: كتابة استجابة نجاح HTTP صغيرة إلى المجرى]
</p>

<p>
	يعرّف أول سطر المتغير <code>response</code> الذي يحتوي بيانات رسالة النجاح، بعدها نستدعي <code>as_bytes</code> على <code>response</code> الخاص بنا لتحويل بيانات السلسلة النصية إلى بايتات. يأخذ تابع <code>write_all</code> على <code>stream</code> النوع <code>‏‏[u8]‏‏&amp;</code> ويرسل هذه البايتات مباشرةً نحو الاتصال لأن عملية <code>write_all</code> قد تفشل. نستعمل <code>unwrap</code> على أي خطأ ناتج كما فعلنا سابقًا. مُجددًا، يجب أن تتعامل مع الأخطاء في التطبيقات الحقيقية.
</p>

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

<h3>
	إعادة HTML حقيقي
</h3>

<p>
	لننفّذ وظيفة إعادة أكثر من صفحة فارغة. أنشئ الملف الجديد hello.html في جذر مسار مشروعك وليس في مسار src. يمكنك إدخال أي HTML تريده، تظهر الشيفرة 4 أحد الاحتمالات.
</p>

<ul>
	<li>
		اسم الملف:hello.html
	</li>
</ul>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1469_16" style=""><span class="dec">&lt;!DOCTYPE html&gt;</span><span class="pln">
</span><span class="tag">&lt;html</span><span class="pln"> </span><span class="atn">lang</span><span class="pun">=</span><span class="atv">"en"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"utf-8"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">Hello!</span><span class="tag">&lt;/title&gt;</span><span class="pln">
  </span><span class="tag">&lt;/head&gt;</span><span class="pln">
  </span><span class="tag">&lt;body&gt;</span><span class="pln">
    </span><span class="tag">&lt;h1&gt;</span><span class="pln">Hello!</span><span class="tag">&lt;/h1&gt;</span><span class="pln">
    </span><span class="tag">&lt;p&gt;</span><span class="pln">Hi from Rust</span><span class="tag">&lt;/p&gt;</span><span class="pln">
  </span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p style="text-align: center;">
	[الشيفرة 4: مثال ملف HTML يعيد استجابة]
</p>

<p>
	يمثّل هذا وثيقة HTML5 بسيطة مع ترويسة وبعض النصوص. سنعدّل الدالة <code>handle_connection</code> لإعادتها من الخادم عندما يُستقبل الطلب، كما في الشيفرة 5 وذلك لقراءة ملف HTML وإضافة الاستجابة مثل متن وإرساله.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1469_19" style=""><span class="pln">use std</span><span class="pun">::{</span><span class="pln">
    fs</span><span class="pun">,</span><span class="pln">
    io</span><span class="pun">::{</span><span class="pln">prelude</span><span class="pun">::*,</span><span class="pln"> </span><span class="typ">BufReader</span><span class="pun">},</span><span class="pln">
    net</span><span class="pun">::{</span><span class="typ">TcpListener</span><span class="pun">,</span><span class="pln"> </span><span class="typ">TcpStream</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">

fn handle_connection</span><span class="pun">(</span><span class="pln">mut stream</span><span class="pun">:</span><span class="pln"> </span><span class="typ">TcpStream</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let buf_reader </span><span class="pun">=</span><span class="pln"> </span><span class="typ">BufReader</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(&amp;</span><span class="pln">mut stream</span><span class="pun">);</span><span class="pln">
    let http_request</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"> buf_reader
        </span><span class="pun">.</span><span class="pln">lines</span><span class="pun">()</span><span class="pln">
        </span><span class="pun">.</span><span class="typ">map</span><span class="pun">(|</span><span class="pln">result</span><span class="pun">|</span><span class="pln"> result</span><span class="pun">.</span><span class="pln">unwrap</span><span class="pun">())</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">take_while</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">line</span><span class="pun">.</span><span class="pln">is_empty</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">

    let status_line </span><span class="pun">=</span><span class="pln"> </span><span class="str">"HTTP/1.1 200 OK"</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="str">"hello.html"</span><span class="pun">).</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">
    let length </span><span class="pun">=</span><span class="pln"> contents</span><span class="pun">.</span><span class="pln">len</span><span class="pun">();</span><span class="pln">

    let response </span><span class="pun">=</span><span class="pln">
        format</span><span class="pun">!(</span><span class="str">"{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}"</span><span class="pun">);</span><span class="pln">

    stream</span><span class="pun">.</span><span class="pln">write_all</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">as_bytes</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;">
	[الشيفرة 5: إرسال محتوى hello.html مثل متن الاستجابة]
</p>

<p>
	أضفنا <code>fs</code> إلى تعليمة <code>use</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> عندما قرأنا محتوى ملف مشروع I/O في الشيفرة 4.
</p>

<p>
	استخدمنا <code>!format</code> لإضافة محتوى الملف على أنه متن استجابة النجاح، أضفنا الترويسة <code>Content-Length</code> التي تحدد حجم متن الاستجابة وفي حالتنا حجم <code>hello.html</code> لضمان استجابة HTTP صالحة لا.
</p>

<p>
	نفذ هذه الشيفرة مع <code>cargo run</code> وحمّل 1270.0.1:7878 في المتصفح، يجب أن ترى HTML الخاص بك معروضًا.
</p>

<p>
	نتجاهل حاليًا طلب البيانات في <code>http_request</code> ونرسل فقط محتوى <a href="https://academy.hsoub.com/programming/html/%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-html-r1702/" rel="">ملف HTML</a> دون شروط، هذا يعني إذا جربنا طلب 127.0.0.1:7878‎/something-else في المتصفح سنحصل على نفس استجابة HTML. في هذه اللحظة الخادم محدود ولا يفعل ما يفعله خوادم الويب، ونريد تعديل استجابتنا اعتمادًا على الطلب وإرسال ملف HTML فقط لطلب منسق جيدًا إلى "/".
</p>

<h3>
	التحقق من صحة الطلب والاستجابة بصورة انتقائية
</h3>

<p>
	يعيد خادم الويب الخاص بنا ملف HTML مهما كان طلب العميل، لنضف وظيفة التحقق أن المتصفح يطلب "/" قبل إعادة ملف HTML وإعادة خطأ في حال طلب المتصفح شيئًا آخر، لذا نحتاج لتعديل <code>handle_connection</code> كما في الشيفرة 6. تتحقق هذه الشيفرة الجديدة محتوى الطلب المُستقبل مع ما يشبه طلب "/" وتضيف كتل <code>if</code> و <code>else</code> لمعالجة الطلبات على نحوٍ مختلف.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1469_21" style=""><span class="com">// --snip--</span><span class="pln">

fn handle_connection</span><span class="pun">(</span><span class="pln">mut stream</span><span class="pun">:</span><span class="pln"> </span><span class="typ">TcpStream</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let buf_reader </span><span class="pun">=</span><span class="pln"> </span><span class="typ">BufReader</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(&amp;</span><span class="pln">mut stream</span><span class="pun">);</span><span class="pln">
    let request_line </span><span class="pun">=</span><span class="pln"> buf_reader</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">unwrap</span><span class="pun">().</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> request_line </span><span class="pun">==</span><span class="pln"> </span><span class="str">"GET / HTTP/1.1"</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let status_line </span><span class="pun">=</span><span class="pln"> </span><span class="str">"HTTP/1.1 200 OK"</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="str">"hello.html"</span><span class="pun">).</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">
        let length </span><span class="pun">=</span><span class="pln"> contents</span><span class="pun">.</span><span class="pln">len</span><span class="pun">();</span><span class="pln">

        let response </span><span class="pun">=</span><span class="pln"> format</span><span class="pun">!(</span><span class="pln">
            </span><span class="str">"{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}"</span><span class="pln">
        </span><span class="pun">);</span><span class="pln">

        stream</span><span class="pun">.</span><span class="pln">write_all</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">as_bytes</span><span class="pun">()).</span><span class="pln">unwrap</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="com">// some other request</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 6: معالجة الطلبات إلى / على نحوٍ مختلف عن الطلبات الأُخرى]
</p>

<p>
	سننظر فقط إلى السطر الأول من طلب HTTP لذا بدلًا من قراءة كامل الطلب لشعاع، نستدعي <code>next</code> ليأخذ العنصر الأول من المكرّر. تتعامل <code>unwarp</code> الأولى مع <code>Option</code> وتوقف البرنامج إذا لم يكن للمكرّر أي عنصر؛ بينما تتعامل <code>unwarp</code> الثانية مع <code>Result</code> ولها نفس تأثير <code>unwarp</code> التي كان في <code>map</code> المضافة في الشيفرة 2.
</p>

<p>
	نتحقق بعد ذلك من <code>request_line</code> لنرى إذا كانت تساوي سطر طلب <code>GET</code> إلى المسار"/"؛ فإذا ساوت تعيد كتلة <code>if</code> محتوى ملف HTML؛ وإذا لم تساوي، يعني ذلك أننا استقبلنا طلب آخر. سنضيف شيفرة إلى كتلة <code>else</code> بعد قليل لاستجابة الطلبات الأخرى.
</p>

<p>
	نفذ هذه الشيفرة واطلب 127.0.0.1:7878، يجب أن تحصل على HTML في hello.html. إذا طلبت أي شيء آخر مثل 127.0.0.1:7878‎/something-else ستحصل على خطأ اتصال مثل الذي تراه عند تنفيذ الشيفرة 1 و2.
</p>

<p>
	لنضيف الشيفرة في الشيفرة 7 إلى كتلة <code>else</code> لإعادة استجابة مع رمز الحالة 404 التي تشير إلى أن محتوى الطلب ليس موجودًا. سنعيد بعض HTML للصفحة لتصّير في المتصفح مشيرةً إلى جواب للمستخدم النهائي.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1469_23" style=""><span class="pln">  </span><span class="com">// --snip--</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">
        let status_line </span><span class="pun">=</span><span class="pln"> </span><span class="str">"HTTP/1.1 404 NOT FOUND"</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="str">"404.html"</span><span class="pun">).</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">
        let length </span><span class="pun">=</span><span class="pln"> contents</span><span class="pun">.</span><span class="pln">len</span><span class="pun">();</span><span class="pln">

        let response </span><span class="pun">=</span><span class="pln"> format</span><span class="pun">!(</span><span class="pln">
            </span><span class="str">"{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}"</span><span class="pln">
        </span><span class="pun">);</span><span class="pln">

        stream</span><span class="pun">.</span><span class="pln">write_all</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">as_bytes</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;">
	[الشيفرة 7: الاستجابة برمز الحالة 404 وصفحة خطأ إذا كان أي شيء عدا / قد طُلب]
</p>

<p>
	لدى استجابتنا سطر حالة مع رمز الحالة 404 وعبارة سبب <code>NOT FOUND</code>، يكون متن الاستجابة HTML في الملف ‎404.html. نحن بحاجة انشاء ملف ‎404.html بجانب hello.html لصفحة الخطأ، ويمكنك استخدام أي HTML تريده أو استخدم مثال HTML في الشيفرة 8.
</p>

<ul>
	<li>
		اسم الملف: ‎404.html
	</li>
</ul>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1469_25" style=""><span class="dec">&lt;!DOCTYPE html&gt;</span><span class="pln">
</span><span class="tag">&lt;html</span><span class="pln"> </span><span class="atn">lang</span><span class="pun">=</span><span class="atv">"en"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"utf-8"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">Hello!</span><span class="tag">&lt;/title&gt;</span><span class="pln">
  </span><span class="tag">&lt;/head&gt;</span><span class="pln">
  </span><span class="tag">&lt;body&gt;</span><span class="pln">
    </span><span class="tag">&lt;h1&gt;</span><span class="pln">Oops!</span><span class="tag">&lt;/h1&gt;</span><span class="pln">
    </span><span class="tag">&lt;p&gt;</span><span class="pln">Sorry, I don't know what you're asking for.</span><span class="tag">&lt;/p&gt;</span><span class="pln">
  </span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p style="text-align: center;">
	[الشيفرة 8: محتوى معين لصفحة كي تُرسل مع أي استجابة 404]
</p>

<p>
	شغّل الخادم مجددًا بعد هذه التغيرات. يجب أن يعيد محتوى hello.html عند طلب 127.0.0.1:7878 ويعيد خطأ HTML من ‎404.html في حال طلب آخر مثل 127.0.0.1:7878‎/foo.
</p>

<h3>
	القليل من إعادة بناء التعليمات البرمجية
</h3>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1469_27" style=""><span class="com">// --snip--</span><span class="pln">

fn handle_connection</span><span class="pun">(</span><span class="pln">mut stream</span><span class="pun">:</span><span class="pln"> </span><span class="typ">TcpStream</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 </span><span class="pun">(</span><span class="pln">status_line</span><span class="pun">,</span><span class="pln"> filename</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"> request_line </span><span class="pun">==</span><span class="pln"> </span><span class="str">"GET / HTTP/1.1"</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="pun">(</span><span class="str">"HTTP/1.1 200 OK"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"hello.html"</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="pun">(</span><span class="str">"HTTP/1.1 404 NOT FOUND"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"404.html"</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">filename</span><span class="pun">).</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">
    let length </span><span class="pun">=</span><span class="pln"> contents</span><span class="pun">.</span><span class="pln">len</span><span class="pun">();</span><span class="pln">

    let response </span><span class="pun">=</span><span class="pln">
        format</span><span class="pun">!(</span><span class="str">"{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}"</span><span class="pun">);</span><span class="pln">

    stream</span><span class="pun">.</span><span class="pln">write_all</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">as_bytes</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;">
	[الشيفرة 9: إعادة بناء التعليمات البرمجية لكتل <code>if</code> و <code>else</code> لتحتوي فقط على الشيفرة المختلفة بين الحالتين]
</p>

<p>
	تعيد الآن كتلتا <code>if</code> و <code>else</code> فقط القيم المناسبة لسطر الحالة واسم الملف في الصف. نستخدم بعد ذلك التفكيك لتحديد هذه القيمتين إلى <code>status_line</code> و <code>filename</code> باستخدام الأنماط في تعليمة <code>let</code> كما تحدثنا في الفصل 18.
</p>

<p>
	الشيفرة المتكررة الآن هي خارج كتلتي <code>if</code> و <code>else</code> وتستخدم المتغيران <code>status_line</code> و <code>filename</code>. يسهّل هذا مشاهدة الفرق بين الحالتين ولدينا فقط مكان واحد لتعديل الشيفرة إذا أردنا تغيير كيفية قراءة الملفات وكتابة الاستجابة. سيكون سلوك الشيفرة في الشيفرة 9 مثل ماهو في الشيفرة 8.
</p>

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

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

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch20-00-final-project-a-web-server.html" rel="external nofollow">Final Project: Building a Multithreaded Web Server</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%A8%D9%86%D8%A7%D8%A1-%D8%AE%D8%A7%D8%AF%D9%85-%D9%88%D9%8A%D8%A8-%D9%85%D8%AA%D8%B9%D8%AF%D8%AF-%D9%85%D9%87%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%AB%D8%A7%D9%86%D9%8A-r2151/" rel="">بناء خادم ويب متعدد مهام المعالجة بلغة رست - الجزء الثاني</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%A7%D9%83%D8%B1%D9%88-macros-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2149/" rel="">الماكرو Macros في لغة رست</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AE%D8%A7%D8%AF%D9%85-%D9%88%D9%8A%D8%A8-%D9%81%D9%8A-nodejs-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A9-http-r1745/" rel="">إنشاء خادم ويب في Node.js باستخدام الوحدة HTTP</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/%D8%AF%D9%84%D9%8A%D9%84-%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%AE%D8%A7%D8%AF%D9%85-%D9%88%D9%8A%D8%A8-%D9%85%D8%AD%D9%84%D9%8A-%D8%AE%D8%B7%D9%88%D8%A9-%D8%A8%D8%AE%D8%B7%D9%88%D8%A9-r422/" rel="">دليل إعداد خادم ويب محلي خطوة بخطوة</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2150</guid><pubDate>Fri, 20 Oct 2023 13:03:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x645;&#x627;&#x643;&#x631;&#x648; Macros &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A;</title><link>https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%A7%D9%83%D8%B1%D9%88-macros-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2149/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_10/-Macros----Rust.png.286fc3617bccb49097709c935a5b7f5e.png" /></p>
<p>
	استخدمنا الماكرو مثل <code>println!‎</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>، إلا أننا لم نتحدث بالكامل عما هو الماكرو وكيفية عمله، إذ تشير كلمة ماكرو إلى مجموعة من الميّزات في رست، ألا وهي الماكرو التصريحية declarative مع <code>macro_rules!‎</code>، إضافةً إلى ثلاثة أنواع من الماكرو الإجرائي procedural:
</p>

<ul>
	<li>
		ماكرو <code>[derive]#</code> مخصص يحدد شيفرة مضافة بسمة <code>derive</code> المستخدمة على الهياكل والمعدّدات.
	</li>
	<li>
		ماكرو شبيه بالسمة attribute، الذي يعرف سمات معينة تُستخدم على أية عنصر.
	</li>
	<li>
		ماكرو يشبه الدالة ويشابه استدعاءات الدالة ولكن يعمل على المفاتيح المحددة مثل وسائطها.
	</li>
</ul>

<p>
	سنتحدث عن كلِّ مما سبق بدوره ولكن لنتحدث أولًا عن حاجتنا للماكرو بالرغم من وجود الدوال.
</p>

<h2>
	الفرق بين الماكرو والدوال
</h2>

<p>
	الماكرو هو طريقة لكتابة شيفرة تكتب شيفرة أُخرى والمعروف بالبرمجة الوصفية metaprogramming، وحدثنا في الملحق "ت" عن سمة <code>derive</code> التي تنشئ تنفيذًا لسمات متعددة، واستخدمنا أيضًا ماكرو <code>‎‎‎‎‎println‎!‎‎‎</code> و <code>‎vec!‎</code> سابقًا. تتوسع كل هذه الماكرو لتضيف شيفرةً أكثر من الشيفرة التي كُتبت يدويًا.
</p>

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

<p>
	يجب أن تصرّح بصمة الدالة signature على عدد ونوع المعاملات الموجودة في الدالة، أما في حالة الماكرو فيمكن أن يأخذ عدد متغير من المعاملات، إذ يمكننا استدعاء <code>println!("hello")‎</code> بوسيط واحد أو <code>println!("hello {}", name)‎</code> بوسيطين. يتوسع أيضًا الماكرو قبل أن يفسر المصرف معنى الشيفرة لذا يمكن للماكرو مثلًا تنفيذ سمة على أي نوع مُعطى ولا يمكن للدالة فعل ذلك لأنها تُستدعى وقت التنفيذ وتحتاج لسمة لتُنفذ وقت التصريف.
</p>

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

<h2>
	الماكرو التصريحي مع macro_rules!‎ للبرمجة الوصفية العامة
</h2>

<p>
	أكثر أنواع الماكرو استخدامًا في رست هو الماكرو التصريحي الذي يسمى أحيانًا "ماكرو بالمثال macros by example" أو "ماكرو <code>macro_rules!‎</code>" أو ببساطة "ماكرو". يسمح لك الماكرو التصريحي بكتابة شيء مشابه لتعبير <code>match</code> في رست بداخله. تعابير <code>match</code> -كما تحدثنا في المقال <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 للتحكم بسير برامج لغة رست</a>- هي هياكل تحكم تقبل تعبيرًا وتقارن القيمة الناتجة من التعبير مع النمط وبعدها تنفذ الشيفرة المرتبطة مع النمط المُطابق. يقارن الماكرو أيضًا قيمةً مع أنماط مرتبطة بشيفرة معينة، وتكون القيمة في هذه الحالة هي الشيفرة المصدرية لرست المُمَررة إلى الماكرو. تُقارن الأنماط مع هيكل الشيفرة المصدرية والشيفرة المرتبطة بكل نمط، وعند حدوث التطابق يستبدل الشيفرة المُمَررة إلى الماكرو، ويحصل كل ذلك وقت التصريف.
</p>

<p>
	نستخدم بنية <code>macro_rules!‎</code> لتعريف الماكرو. دعنا نتحدث عن كيفية استخدام <code>macro_rules!‎</code> بالنظر إلى كيفية تعريف ماكرو <code>vec!‎</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>vec!‎</code> من أجل إنشاء شعاع جديد بقيم معينة. ينشئ الماكرو التالي مثلًا شعاع جديد يحتوي على ثلاثة أعداد صحيحة.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6078_8" style=""><span class="pln">let v</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vec</span><span class="str">&lt;u32&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></pre>

<p>
	يمكن استخدام الماكرو <code>vec!‎</code> لإنشاء شعاع بعددين صحيحين أو شعاع بخمس سلاسل شرائح نصية string slice، ولا يمكننا فعل ذلك باستخدام الدوال لأننا لا نعرف عدد أو نوع القيم مسبقًا.
</p>

<p>
	تبين الشيفرة 28 تعريفًا مبسطًا لماكرو <code>vec!‎</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6078_10" style=""><span class="com">#[macro_export]</span><span class="pln">
macro_rules</span><span class="pun">!</span><span class="pln"> vec </span><span class="pun">{</span><span class="pln">
    </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">expr </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">{</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
            let mut temp_vec </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="pun">(</span><span class="pln">
                temp_vec</span><span class="pun">.</span><span class="pln">push</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">
            temp_vec
        </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;">
	[الشيفرة 28: نسخة مبسطة من تعريف ماكرو <code>vec!‎</code>]
</p>

<p>
	<strong>ملاحظة</strong>: يتضمن التعريف الفعلي لماكرو <code>vec!‎</code> في المكتبة القياسية شيفرة للحجز الصحيح للذاكرة مسبقًا، وهذه الشيفرة هي تحسين لم نضفه هنا لجعل المثال أبسط.
</p>

<p>
	يشير توصيف <code>‏[macro_export]#‏‎</code> إلى أن هذا الماكرو يجب أن يبقى متاحًا عندما يجري إحضار الوحدة المُصرّفة crate المعرّفة داخلها الماكرو إلى النطاق، ولا يمكن إضافة الماكرو إلى النطاق دون هذا التوصيف.
</p>

<p>
	عندما نبدأ بتعريف الماكرو مع <code>macro_rules!‎</code> ويكون اسم الماكرو الذي نعرّفه بدون علامة التعجب، يكون الاسم في هذه الحالة <code>vec</code> متبوعًا بقوسين معقوصين تدل على متن تعريف الماكرو.
</p>

<p>
	يشابه الهيكل في متن <code>vec!‎</code> الهيكل في تعبير <code>match</code>، إذ لدينا هنا ذراع واحد مع النمط <code>‎( $( $x:expr ),* )‎‏‎‎‎</code> متبوعةً بالعامل <code>‎=&gt;‎</code> وكتلة الشيفرة المرتبطة في النمط، وستُرسل الكتلة المرتبطة إذا تطابق النمط. بما أن هذا هو النمط الوحيد في الماكرو، هناك طريقة وحيدة للمطابقة، وأي أنماط أُخرى ستسبب خطأ، ويكون لدى الماكرو الأكثر تعقيدًا أكثر من ذراع واحدة.
</p>

<p>
	تختلف الصيغة الصحيحة في تعاريف الماكرو عن صيغة النمط المذكور سابقًا في المقال <a href="https://academy.hsoub.com/programming/rust/%D8%B5%D9%8A%D8%A7%D8%BA%D8%A9-%D8%A3%D9%86%D9%85%D8%A7%D8%B7-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D8%B5%D8%AD%D9%8A%D8%AD%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2110/" rel="">صياغة أنماط التصميم الصحيحة Pattern Syntax في لغة رست</a> لأن أنماط الماكرو تُطابق مع هيكل شيفرة رست بدلًا من القيم. لنتحدث عن ماذا تعني أقسام النمط في الشيفرة 28. لقراءة صيغة نمط ماكرو الكاملة راجع <a href="https://doc.rust-lang.org/stable/reference/macros-by-example.html" rel="external nofollow">مرجع رست</a>.
</p>

<p>
	استخدمنا أولًا مجموعة أقواس لتغليف كامل النمط، واستخدمنا علامة الدولار (<code>$</code>) للتصريح عن متغير في نظام الماكرو الذي يحتوي على شيفرة رست مطابقة للنمط، إذ توضح إشارة الدولار أن هذا متغير ماكرو وليس متغير رست عادي. تأتي بعد ذلك مجموعةٌ من الأقواس التي تلتقط القيم التي تطابق النمط داخل القوسين لاستخدامها في الشيفرة المُستبدلة. توجد <code>‎$x:expr</code> داخل <code>‎$()‎‎</code>، التي تطابق أي تعبير رست وتعطي التعبير الاسم <code>‎$x</code>. تشير الفاصلة التي تلي <code>‎‎‎$()‎</code> أنه يمكن أن يظهر هناك محرف فاصلة بعد الشيفرة الذي يطابق الشيفرة في <code>‎$()‎</code>، وتشير <code>*</code> إلى أن هناك نمط يطابق صفر أو أكثر مما يسبق <code>*</code>.
</p>

<p>
	عندما نستدعي هذا الماكرو باستخدام <code>vec![1, 2, 3];‎</code>، يُطابق النمط <code>‎$x‎</code> ثلاث مرات مع التعابير الثلاث 1 و 2 و 3.
</p>

<p>
	لننظر إلى النمط الموجود في متن الشيفرة المرتبطة مع هذا الذراع، إذ تُنشَئ <code>temp_vec.push()‎</code> داخل <code>‎$()*‎</code> لكل جزء يطابق <code>‎$()‎</code> في النمط صفر مرة أو أكثر اعتمادًا على كم مرة طابق النمط. تُبَدل <code>‎$‎x</code> مع كل جزء مطابق، وعندما نستدعي الماكرو باستخدام <code>vec![1, 2, ‎3];‎</code>، ستكون الشيفرة المُنشأة التي تستبدل هذا الماكرو على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6078_12" style=""><span class="pun">{</span><span class="pln">
    let mut temp_vec </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">
    temp_vec</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
    temp_vec</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="lit">2</span><span class="pun">);</span><span class="pln">
    temp_vec</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="lit">3</span><span class="pun">);</span><span class="pln">
    temp_vec
</span><span class="pun">}</span></pre>

<p>
	عرّفنا الماكرو الذي يستطيع أن يأخذ أي عدد من الوسطاء من أي نوع ويستطيع إنشاء شيفرة لإنشاء شعاع يحتوي العناصر المحددة.
</p>

<p>
	لتعرف أكثر عن كيفية كتابة الماكرو، راجع وثائق ومصادر أُخرى على الشبكة مثل "<a href="https://veykril.github.io/tlborm/" rel="external nofollow">الكتاب الصغير لماكرو رست The Little Book of Rust Macros</a>" الذي بدأ فيه دانيل كيب Daniel Keep وتابعه لوكاس ويرث Lukas Wirth.
</p>

<h2>
	الماكرو الإجرائي لإنشاء شيفرة من السمات
</h2>

<p>
	الشكل الثاني من الماكرو هو الماكرو الإجرائي الذي يعمل أكثر مثل دالة (وهي نوع من الإجراءات). يقبل الماكرو الإجرائي بعض الشيفرة مثل دخل ويعمل على الشيفرة ويُنتج بعض الشيفرة مثل خرج بدلًا من مطابقة الأنماط وتبديل الشيفرة بشيفرة أُخرى كما يعمل الماكرو التصريحي. أنواع الماكرو الإجرائي الثلاث، هي: مشتقة مخصصة custom derive، أو مشابهة للسمة attribute-like، أو مشابهة للدالة function-like وتعمل كلها بطريقة مشابهة.
</p>

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

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

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

</span><span class="com">#[some_attribute]</span><span class="pln">
pub fn some_name</span><span class="pun">(</span><span class="pln">input</span><span class="pun">:</span><span class="pln"> </span><span class="typ">TokenStream</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">TokenStream</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 29: مثال لتعريف ماكرو إجرائي]
</p>

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

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

<h2>
	كيفية كتابة ماكرو derive مخصص
</h2>

<p>
	لننشئ وحدة مصرّفة اسمها <code>hello_macro</code> التي تعرف سمةً اسمها <code>HelloMacro</code> مع دالة مرتبطة associated اسمها <code>hello_macro</code>، وبدلًا من إجبار المستخدمين على تنفيذ السمة <code>HelloMacro</code> لكل من أنواعهم، سنؤمن ماكرو إجرائي لكي يتمكن المستخدمين من توصيف نوعهم باستخدام <code>‏‏‏[derive(HelloMacro)‎]‏‏#</code> للحصول على تنفيذ افتراضي للدالة <code>hello_macro</code>.
</p>

<p>
	سيطبع النفيذ الافتراضي:
</p>

<pre class="ipsCode">Hello, Macro! My name is TypeName!‎
</pre>

<p>
	إذ أن <code>TypeName</code> هو اسم النوع المُعرّفة عليه السمة، بمعنى آخر سنكتب وحدة مصرّفة تسمح لمبرمج آخر بكتابة الشيفرة باستخدام حزمتنا المصرفة كما في الشيفرة 30.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6078_16" style=""><span class="pln">use hello_macro</span><span class="pun">::</span><span class="typ">HelloMacro</span><span class="pun">;</span><span class="pln">
use hello_macro_derive</span><span class="pun">::</span><span class="typ">HelloMacro</span><span class="pun">;</span><span class="pln">

</span><span class="com">#[derive(HelloMacro)]</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Pancakes</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="typ">Pancakes</span><span class="pun">::</span><span class="pln">hello_macro</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<p>
	ستطبع الشيفرة عندما تنتهي ما يلي:
</p>

<pre class="ipsCode">Hello, Macro! My name is Pancakes!‎
</pre>

<p>
	الخطوة الأولى هي إنشاء وحدة مكتبة مصرّفة على النحو التالي:
</p>

<pre class="ipsCode">$ cargo new hello_macro --lib
</pre>

<p>
	بعدها نعرّف سمة <code>HelloMacro</code> والدّالة التابعة لها.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6078_18" style=""><span class="pln">pub trait </span><span class="typ">HelloMacro</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn hello_macro</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_6078_20" style=""><span class="pln">use hello_macro</span><span class="pun">::</span><span class="typ">HelloMacro</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Pancakes</span><span class="pun">;</span><span class="pln">

impl </span><span class="typ">HelloMacro</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">Pancakes</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn hello_macro</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">"Hello, Macro! My name is Pancakes!"</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">
    </span><span class="typ">Pancakes</span><span class="pun">::</span><span class="pln">hello_macro</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<p>
	الخطوة التالية هي تعريف الماكرو الإجرائي. يحتاج الماكرو الإجرائي حتى الآن إلى وحدة مصرّفة خاصة به، ربما سيُرفع هذا التقييد بالنهاية. يأتي اصطلاح الوحدات المصرّفة الهيكلية والوحدات المصرّفة للماكرو على النحو التالي: يسمى الماكرو الإجرائي الخاص المشتق <code>foo_derive</code> لاسم موحدة مصرفة <code>foo</code>. لنبدأ بإنشاء وحدة مصرّفة جديدة اسمها <code>hello_macro_derive</code> داخل المشروع <code>hello_macro</code>.
</p>

<pre class="ipsCode">$ cargo new hello_macro_derive --lib
</pre>

<p>
	الوحدتان المصرّفتان مرتبطتان جدًا، لذلك سننشئ وحدةً مصرّفةً للماكرو الإجرائي داخل مجلد الوحدة المصرّفة <code>hello_macro</code>. يجب علينا تغيير تنفيذ الماكرو الإجرائي في <code>hello_macro_derive</code> إذا غيرنا تعريف السمة في <code>hello_macro</code> أيضًا. تحتاج الوحدتان المصرّفتان أن تُنشَرا بصورةٍ منفصلة ويجب أن يضيف مستخدمو هاتين الوحدتين المصرّفتين مثل اعتماديتين dependencies وجلبهما إلى النطاق. يمكن -بدلًا من ذلك- جعل الحزمة المصرّفة <code>hello_macro</code> تستخدم <code>hello_macro_derive</code> مثل اعتمادية وتعيد تصدير شيفرة الماكرو الإجرائي ولكن الطريقة التي بنينا فيها المشروع تسمح للمبرمجين استخدام <code>hello_macro</code> حتى لو كانوا لا يرغبون باستخدام وظيفة <code>derive</code>.
</p>

<p>
	يجب علينا التصريح عن الوحدة المصرفة <code>hello_macro_derive</code> مثل وحدة مصرفة لماكرو إجرائي ونحتاج أيضًا إلى وظائف من الوحدات المصرّفة <code>syn</code> و <code>quote</code> كما سنرى بعد قليل لذا سنحتاج لإضافتهم كاعتماديات. أضِف التالي إلى ملف Cargo.toml من أجل <code>hello_macro_derive</code>:
</p>

<ul>
	<li>
		اسم الملف: hello_macro_derive/Cargo.toml
	</li>
</ul>

<pre class="ipsCode">[lib]
proc-macro = true

[dependencies]
syn = "1.0"
quote = "1.0"
</pre>

<p>
	لنبدأ بتعريف الماكرو الإجرائي. ضع الشيفرة 31 في ملف src/lib.rs من أجل الوحدة المصرّفة <code>hello_macro-derive</code>. لاحظ أن الشيفرة لن تصرّف حتى نضيف التعريف لدالة <code>impl_hello_macro</code>.
</p>

<ul>
	<li>
		اسم الملف: hello_macro_derive/src/lib.rs
	</li>
</ul>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6078_23" style=""><span class="pln">use proc_macro</span><span class="pun">::</span><span class="typ">TokenStream</span><span class="pun">;</span><span class="pln">
use quote</span><span class="pun">::</span><span class="pln">quote</span><span class="pun">;</span><span class="pln">
use syn</span><span class="pun">;</span><span class="pln">

</span><span class="com">#[proc_macro_derive(HelloMacro)]</span><span class="pln">
pub fn hello_macro_derive</span><span class="pun">(</span><span class="pln">input</span><span class="pun">:</span><span class="pln"> </span><span class="typ">TokenStream</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">TokenStream</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// إنشاء تمثيل لشيفرة رست مثل شجرة صيغة يمكننا التلاعب بها </span><span class="pln">
    let ast </span><span class="pun">=</span><span class="pln"> syn</span><span class="pun">::</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">input</span><span class="pun">).</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">
    </span><span class="com">// بناء تنفيذ السمة</span><span class="pln">
    impl_hello_macro</span><span class="pun">(&amp;</span><span class="pln">ast</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 31: الشيفرة التي تتطلبها معظم الوحدات المصرّفة للماكرو الإجرائي لكي تعالج شيفرة رست]
</p>

<p>
	لاحظ أننا قسّمنا الشيفرة إلى دالة <code>hello_macro_derive</code> المسؤولة عن تحليل <code>TokenStream</code>، ودالة <code>Impl_hello_macro</code> المسؤولة عن تحويل شجرة الصيغة syntax tree التي تجعل كاتبة الماكرو الإجرائي لتكون أكثر ملائمة. ستكون الشيفرة في الدالة الخارجية (في هذه الحالة <code>hello_macro_derive</code>) هي نفسها لمعظم الوحدات المصرّفة للماكرو الإجرائي الذي تراه أو تنشئه، وستكون الشيفرة التي تحددها في محتوى الدالة الداخلية (في هذه الحالة <code>impl_hello_macro</code>) مختلفة اعتمادًا على غرض الماكرو الإجرائي.
</p>

<p>
	أضفنا ثلاث وحدات مصرّفة هي <code>proc_macro</code> و <code>syn</code> و <code>quote</code>. لا نحتاج لإضافة الوحدة المصرفة <code>proc_macro</code> إلى الاعتماديات في Cargo.toml لأنها تأتي مع رست، وهذه الوحدة المصرفة هي واجهة برمجة التطبيق للمصرف التي تسمح بقراءة وتعديل شيفرة رست من شيفرتنا.
</p>

<p>
	تحلّل الوحدة المصرّفة <code>syn</code> شيفرة رست من سلسلة نصية إلى هيكل بيانات يمكننا إجراء عمليات عليه. تحوّل الوحدة المصرّفة <code>quote</code> هيكل بيانات <code>syn</code> إلى شيفرة رست. تسهّل هذه الوحدات المصرّفة تحليل أي نوع من شيفرة رست يمكن أن نعمل عليه. تُعد كتابة محلل parser كامل لرست أمرًا صعبًا.
</p>

<p>
	تُستدعى دالة <code>hello_macro_derive</code> عندما يحدد مستخدم مكتبتنا <code>[derive(HelloMacro)‎]#</code> على نوع، وهذا ممكن لأننا وصفّنا دالة <code>hello_macro_dervie</code> باستخدام <code>proc_macro_dervie</code> وحددنا اسم <code>HelloMacro</code> الذي يطابق اسم سِمتنا، وهذا هو الاصطلاح الذي يتبعه معظم الماكرو الإجرائي.
</p>

<p>
	تحوّل دالة <code>hello_macro_derive</code> أولًا <code>input</code> من <code>TokenStream</code> إلى هيكل بيانات يمكن أن نفسره ونجري عمليات عليه. هنا يأتي دور <code>syn</code>. تأخذ دالة <code>parse</code> في <code>syn</code> القيمة <code>TokenStream</code> وتُعيد هيكل <code>DeriveInput</code> يمثّل شيفرة رست المحلّلة. تظهر الشيفرة 32 الأجزاء المهمة من هيكل <code>DeriveInput</code> التي نحصل عليها من تحليل السلسلة النصية <code>struct Pancakes;‎</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6078_25" style=""><span class="typ">DeriveInput</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">

    ident</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Ident</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        ident</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Pancakes"</span><span class="pun">,</span><span class="pln">
        span</span><span class="pun">:</span><span class="pln"> </span><span class="com">#0 bytes(95..103)</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    data</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Struct</span><span class="pun">(</span><span class="pln">
        </span><span class="typ">DataStruct</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            struct_token</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Struct</span><span class="pun">,</span><span class="pln">
            fields</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Unit</span><span class="pun">,</span><span class="pln">
            semi_token</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">
                </span><span class="typ">Semi</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;">
	[الشيفرة 32: نسخة <code>DeriveInput</code> التي نحصل عليها من تحليل الشيفرة التي فيها سمة الماكرو في الشيفرة 30]
</p>

<p>
	تظهر حقول هذا الهيكل بأن شيفرة رست التي حللناها هي هيكل وحدة مع <code>ident</code> (اختصارًا للمعرّف، أي الاسم) الخاصة بالاسم <code>Pancakes</code>. هناك حقول أخرى في هذا الهيكل لوصف كل أنواع شيفرة رست. راجع <a href="https://docs.rs/syn/1.0/syn/struct.DeriveInput.html" rel="external nofollow">وثائق <code>syn</code></a> من أجل <code>DeriveInput</code> لمعلومات أكثر.
</p>

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

<p>
	ربما لاحظت أننا استدعينا <code>unwrap</code> لتجعل الدالة <code>hello_macro_derive</code> تهلع إذا فشل استدعاء الدالة <code>syn::parse</code>. يجب أن يهلع الماكرو الإجرائي على الأخطاء، لأنه يجب أن تعيد الدالة <code>proc_macro_derive</code> الـقيمة <code>TokenStream</code> بدلًا من <code>Result</code> لتتوافق مع واجهة برمجة التطبيقات للماكرو الإجرائي. بسّطنا هذا المثال باستخدام <code>unwrap</code>، إلا أنه يجب تأمين رسالة خطأ محددة أكثر في شيفرة الإنتاج باستخدام <code>panic!‎</code> أو <code>expect</code>.
</p>

<p>
	الآن لدينا الشيفرة لتحويل شيفرة رست الموصّفة من <code>TokenStream</code> إلى نسخة <code>DeriveInput</code> لننشئ الشيفرة التي تطبّق سمة <code>HelloMacro</code> على النوع الموصّف كما تظهر الشيفرة 33.
</p>

<ul>
	<li>
		اسم الملف: hello_macro_derive/src/lib.rs
	</li>
</ul>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6078_27" style=""><span class="pln">fn impl_hello_macro</span><span class="pun">(</span><span class="pln">ast</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">syn</span><span class="pun">::</span><span class="typ">DeriveInput</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">TokenStream</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let name </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">ast</span><span class="pun">.</span><span class="pln">ident</span><span class="pun">;</span><span class="pln">
    let gen </span><span class="pun">=</span><span class="pln"> quote</span><span class="pun">!</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        impl </span><span class="typ">HelloMacro</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="com">#name {</span><span class="pln">
            fn hello_macro</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">"Hello, Macro! My name is {}!"</span><span class="pun">,</span><span class="pln"> stringify</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="pun">}</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
    gen</span><span class="pun">.</span><span class="pln">into</span><span class="pun">()</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 33: تنفيذ سمة <code>HelloMacro</code> باستخدام شيفرة رست المحلّلة]
</p>

<p>
	نحصل على نسخة هيكل <code>Indent</code> يحتوي على الاسم (المُعرّف) على النوع الموصّف باستخدام <code>ast.ident</code>. يظهر الهيكل في الشيفرة 32 أنه عندما نفّذنا دالة <code>impl_hello_macro</code> على الشيفرة 30 سيكون لدى <code>ident</code> التي نحصل عليها حقل <code>ident</code> مع القيمة <code>"Pancakes"</code>، لذلك سيحتوي المتغير <code>name</code> في الشيفرة 33 نسخة هيكل <code>Ident</code>، الذي سيكون سلسلة نصية <code>"Pancakes"</code> عندما يُطبع، وهو اسم الهيكل في الشيفرة 30.
</p>

<p>
	يسمح لنا ماكرو <code>quote!‎</code> بتعريف شيفرة رست التي نريد إعادتها. يتوقع المصرف شيئًا مختلفًا عن النتيجة المباشرة لتنفيذ ماكرو <code>quote!‎</code>، لذا نحتاج لتحويله إلى <code>TokenStream</code>، وذلك عن طريق استدعاء تابع <code>into</code> الذي يستهلك التعبير الوسطي ويعيد القيمة من النوع <code>TokenStream</code> المطلوب.
</p>

<p>
	يؤمن ماكرو <code>quote!‎</code> تقنيات قولبة templating جيدة، إذ يمكننا إدخال <code>‎#‎‎name</code> ويبدّلها <code>quote!‎</code> بالقيمة الموجودة في المتغير <code>name</code>، ويمكنك أيضًا إجراء بعض التكرارات بطريقة مشابهة لكيفية عمل الماكرو العادي. راجع <a href="https://docs.rs/quote" rel="external nofollow">توثيق الوحدة المصرّفة <code>quote</code></a> لتعريف وافي عنها.
</p>

<p>
	نريد أن يُنشئ الماكرو الإجرائي تنفيذًا لسمة <code>HelloMacro</code> للنوع الذي يريد توصيفه المستخدم، والذي نحصل عليه باستخدام <code>‎#‎‎name</code>. يحتوي تنفيذ السمة دالةً واحدةً <code>hello_macro</code> تحتوي على الوظيفة المراد تقديمها ألا وهي طباعة <code>Hello, ‎Macro! My name is</code> وبعدها اسم النوع الموصَّف.
</p>

<p>
	الماكرو <code>stringify!‎</code> المُستخدم هنا موجود داخل رست، إذ يأخذ تعبير رست مثل <code>1‎ + 2‎</code> ويحول التعبير إلى سلسلة نصية مجرّدة مثل <code>"‎1 +2"</code>. هذا مختلف عن <code>format!‎</code> و <code>println!‎</code>، الماكرو الذي يقيّم التعبير ويحول القيمة إلى <code>String</code>. هناك احتمال أن يكون الدخل <code>‎#‎name</code> تعبيرًا للطباعة حرفيًا literally، لذا نستخدم <code>stringify!‎</code>، الذي يوفر مساحةً محجوزةً عن طريق تحويل <code>‎‎#‎name</code> إلى سلسلة نصية مجرّدة وقت التصريف.
</p>

<p>
	الآن، يجب أن ينتهي <code>cargo build</code> بنجاح في كل من <code>hello_macro</code> و <code>hello_macro_derive</code>. لنربط هذه الوحدات المصرّفة مع الشيفرة في الشيفرة 30 لنرى كيفية عمل الماكرو الإجرائي. أنشئ مشروعًا ثنائيًا جديدًا في مجلد المشاريع باستخدام <code>cargo new pancakes</code>. نحتاج لإضافة <code>hello_macro</code> و <code>hello_macro_derive</code> مثل اعتماديات في ملف Cargo.toml الخاص بالوحدة المصرّفة <code>pancakes</code>. إذا نشرت النسخ الخاصة بك من <code>hello_macro</code> و <code>hello_macro_derive</code> إلى crates.io فستكون اعتماديات عادية، وإذا لم يكونوا كذلك فبإمكانك تحديدها مثل اعتماديات <code>path</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6078_29" style=""><span class="pln">hello_macro </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">"../hello_macro"</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
hello_macro_derive </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">"../hello_macro/hello_macro_derive"</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	ضع الشيفرة 30 في الملف src/main.rs ونفذ <code>cargo run</code> يجب أن تطبع <code>Hello, Macro! My name is Pancakes!‎</code>. كان تنفيذ سمة <code>HelloMacro</code> من الماكرو الإجرائي متضمنًا دون أن تحتاج الوحدة المصرفة <code>pancakes</code> أن تنفّذه. أضاف <code>[‎‎derive(HelloMac‎ro)‎]#</code> تنفيذ السمة.
</p>

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

<h2>
	الماكرو الشبيه بالسمة
</h2>

<p>
	يشابه الماكرو الشبيه بالسمة الماكرو المشتق الخاص لكن بدلًا من إنشاء شيفرة لسمة <code>derive</code> يسمح لك بإنشاء سمات جديدة وهي أيضًا أكثر مرونة، تعمل <code>derive</code> فقط مع الهياكل والـتعدادات enums، يمكن أن تطبق السمات attributes على عناصر أُخرى أيضًا مثل الدوال. فيما يلي مثال عن استخدام الماكرو الشبيه بالسمة: لنقل أن لديك سمة اسمها <code>route</code> توصّف الدوال عند استخدام إطار عمل تطبيق ويب:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6078_31" style=""><span class="com">#[route(GET, "/")]</span><span class="pln">
fn index</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span></pre>

<p>
	تُعرَّف سمة <code>‏‏[route]#</code> بإطار العمل مثل ماكرو إجرائي. ستكون بصمة دالة تعريف الماكرو على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6078_33" style=""><span class="com">#[proc_macro_attribute]</span><span class="pln">
pub fn route</span><span class="pun">(</span><span class="pln">attr</span><span class="pun">:</span><span class="pln"> </span><span class="typ">TokenStream</span><span class="pun">,</span><span class="pln"> item</span><span class="pun">:</span><span class="pln"> </span><span class="typ">TokenStream</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">TokenStream</span><span class="pln"> </span><span class="pun">{</span></pre>

<p>
	لدينا هنا معاملان من النوع <code>TokenStream</code>، الأول هو من أجل محتوى السمة (جزء <code>GET, "/"‎</code>)، والثاني هو لمتن العنصر الذي ترتبط به السمة والذي هو <code>fn index‎() {}‎</code> في هذه الحالة والباقي هو متن الدالة.
</p>

<p>
	عدا عن ذلك، تعمل الماكرو الشبيهة بالسمة بنفس طريقة الماكرو المشتق الخاص عن طريق إنشاء وحدة مصرفة مع نوع الوحدة المصرّفة <code>proc-macro</code> وتنفذ الدالة التي تنشئ الشيفرة المرغوبة.
</p>

<h2>
	الماكرو الشبيه بالدالة
</h2>

<p>
	يعرّف الماكرو الشبيه بالدالة الماكرو ليشبه استدعاءات الدوال، وعلى نحوٍ مشابه لماكرو <code>macro_rules!‎</code>، فهي أكثر مرونة من الدوال؛ إذ يستطيع الماكرو أخذ عدد غير معروف من الوسطاء، ولكن يمكن أن يعرّف ماكرو <code>macro_rules!‎</code> فقط باستخدام صيغة تشبه المطابقة التي تحدثنا عنها سابقًا في قسم "الماكرو التصريحي مع macro_rules!‎ للبرمجة الوصفية العامة". يأخذ الماكرو الشبيه بالدالة معامل <code>TokenStream</code> ويعدل تعريفها القيمة <code>TokenStream</code> باستخدام شيفرة رست كما يفعل الماكرو الإجرائي السابق. إليك مثالًا عن ماكرو شبيه بالدالة هو ماكرو <code>sql!‎</code> التي يمكن استدعاؤه على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6078_35" style=""><span class="com">#[proc_macro_attribute]</span><span class="pln">
pub fn route</span><span class="pun">(</span><span class="pln">attr</span><span class="pun">:</span><span class="pln"> </span><span class="typ">TokenStream</span><span class="pun">,</span><span class="pln"> item</span><span class="pun">:</span><span class="pln"> </span><span class="typ">TokenStream</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">TokenStream</span><span class="pln"> </span><span class="pun">{</span></pre>

<p>
	يحلل هذا الماكرو تعليمة SQL داخله ويتحقق إذا كانت صياغتها صحيحة، وهذه المعالجة أعقد مما يستطيع <code>macro_rules!‎</code> معالجته ويكون تعريف ماكرو <code>sql!‎</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6078_37" style=""><span class="com">#[proc_macro]</span><span class="pln">
pub fn sql</span><span class="pun">(</span><span class="pln">input</span><span class="pun">:</span><span class="pln"> </span><span class="typ">TokenStream</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">TokenStream</span><span class="pln"> </span><span class="pun">{</span></pre>

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

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch19-00-advanced-features.html" rel="external nofollow">Advanced Features</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%A8%D9%86%D8%A7%D8%A1-%D8%AE%D8%A7%D8%AF%D9%85-%D9%88%D9%8A%D8%A8-%D9%85%D8%AA%D8%B9%D8%AF%D8%AF-%D9%85%D9%87%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%A3%D9%88%D9%84-r2150/" rel="">بناء خادم ويب متعدد مهام المعالجة بلغة رست - الجزء الأول</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D9%88%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2148/" rel="">الأنواع والدوال المتقدمة في لغة رست</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A3%D9%86%D9%85%D8%A7%D8%B7-patterns-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D8%A7%D8%AA%D9%87%D8%A7-%D9%88%D9%82%D8%A7%D8%A8%D9%84%D9%8A%D8%AA%D9%87%D8%A7-%D9%84%D9%84%D8%AF%D8%AD%D8%B6-refutability-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2109/" rel="">الأنماط Patterns واستخداماتها وقابليتها للدحض Refutability في لغة رست</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%85%D8%A7%D9%83%D8%B1%D9%88-macro-%D9%88%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC-%D8%A7%D9%84%D9%85%D8%B3%D8%A8%D9%82-preprocessor-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1750/" rel="">الماكرو Macro والمعالج المسبق Preprocessor في لغة سي</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2149</guid><pubDate>Thu, 12 Oct 2023 13:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x623;&#x646;&#x648;&#x627;&#x639; &#x648;&#x627;&#x644;&#x62F;&#x648;&#x627;&#x644; &#x627;&#x644;&#x645;&#x62A;&#x642;&#x62F;&#x645;&#x629; &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A;</title><link>https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D9%88%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2148/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_10/-types--functions-----Rust.png.c3cdae0981f58a4515eb723f28a48b2c.png" /></p>
<p>
	نستعرض في هذه المقالة كل من الأنواع types والدوال functions المتقدمة في لغة رست.
</p>

<h2>
	الأنواع المتقدمة
</h2>

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

<h3>
	استخدام نمط النوع الجديد لأمان النوع والتجريد
</h3>

<p>
	<strong>ملاحظة</strong>: يفترض هذا القسم أنك قرأت قسم ''استخدام نمط النوع الجديد لتنفيذ سمات الخارجية على الأنواع الخارجية'' من المقال السابق <a href="https://academy.hsoub.com/programming/rust/%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D8%B9%D9%86-%D8%A7%D9%84%D8%B3%D9%85%D8%A7%D8%AA-trait-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2112/" rel="">مفاهيم متقدمة عن السمات Trait في لغة رست</a>.
</p>

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

<p>
	يمكننا أيضًا استخدام نمط النوع الجديد للتخلص من بعض تفاصيل التطبيق الخاصة بنوع ما، ويمكن أن يكشف النوع الجديد عن واجهة برمجية عامة <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> تختلف عن الواجهة البرمجية للنوع الداخلي الخاص.
</p>

<p>
	يمكن أن تخفي الأنواع الجديدة أيضًا التطبيق الداخلي، إذ يمكننا على سبيل المثال يمكننا منح نوع <code>People</code> لتغليف <code>&lt;HashMap&lt;i32, String</code> الذي يخزن معرف الشخص المرتبط باسمه. تتفاعل الشيفرة التي تستخدم <code>People</code> فقط مع الواجهة البرمجية العامة التي نقدمها مثل تابع لإضافة سلسلة اسم إلى مجموعة <code>People</code>، ولن تحتاج هذه الشيفرة إلى معرفة أننا نعيِّن معرفًا <code>i32</code> للأسماء داخليًا. يعد نمط النوع الجديد طريقةً خفيفةً لتحقيق التغليف لإخفاء تفاصيل التطبيق التي ناقشناها سابقًا في قسم "التغليف وإخفاءه لتفاصيل التنفيذ" من المقال <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-oop-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2097/" rel="">البرمجة كائنية التوجه <abbr title="Object-Oriented Programming | البرمجة كائنية التوجه"><abbr title="Object-Oriented Programming | البرمجة كائنية التوجه">OOP</abbr></abbr> في لغة رست</a>.
</p>

<h3>
	إنشاء مرادفات للنوع بواسطة اسماء النوع البديلة
</h3>

<p>
	توفّر رست القدرة على التصريح عن اسم بديل للنوع type alias لمنح نوع موجود اسمًا آخر، ونستخدم لذلك الكلمة المفتاحية <code>type</code>. يمكننا على سبيل المثال منح الاسم البديل <code>Kilometers</code> للنوع <code>i32</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_8" style=""><span class="pln"> type </span><span class="typ">Kilometers</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> i32</span><span class="pun">;</span></pre>

<p>
	يصبح الاسم المستعار <code>Kilometers</code> الآن مرادفًا للنوع <code>i32</code> على عكس أنواع <code>Millimeters</code> و <code>Meters</code> التي أنشأناها في الشيفرة 15، إذ أن <code>Kilometers</code> ليست نوعًا جديدًا منفصلًا. ستُعامل القيم ذات النوع <code>Kilometers</code> نفس معاملة قيم النوع <code>i32</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_10" style=""><span class="pln">    type </span><span class="typ">Kilometers</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> i32</span><span class="pun">;</span><span class="pln">

    let x</span><span class="pun">:</span><span class="pln"> i32 </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span><span class="pln">
    let y</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Kilometers</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span><span class="pln">

    println</span><span class="pun">!(</span><span class="str">"x + y = {}"</span><span class="pun">,</span><span class="pln"> x </span><span class="pun">+</span><span class="pln"> y</span><span class="pun">);</span></pre>

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

<p>
	حالة الاستخدام الرئيسة لأسماء النوع البديلة هي تقليل التكرار، على سبيل المثال قد يكون لدينا نوع طويل مثل هذا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_12" style=""><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">Fn</span><span class="pun">()</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="typ">Send</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'static&gt;</span></pre>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_14" style=""><span class="pln">    let f</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">Fn</span><span class="pun">()</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="typ">Send</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'</span><span class="kwd">static</span><span class="pun">&gt;</span><span class="pln"> </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="pln"> println</span><span class="pun">!(</span><span class="str">"hi"</span><span class="pun">));</span><span class="pln">

    fn takes_long_type</span><span class="pun">(</span><span class="pln">f</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">Fn</span><span class="pun">()</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="typ">Send</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'</span><span class="kwd">static</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">

    fn returns_long_type</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">Fn</span><span class="pun">()</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="typ">Send</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'</span><span class="kwd">static</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></pre>

<p style="text-align: center;">
	[الشيفرة 24: استعمال نوع طويل في أماكن كثيرة]
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_16" style=""><span class="pln">    type </span><span class="typ">Thunk</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">Fn</span><span class="pun">()</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="typ">Send</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'</span><span class="kwd">static</span><span class="pun">&gt;;</span><span class="pln">

    let f</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Thunk</span><span class="pln"> </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="pln"> println</span><span class="pun">!(</span><span class="str">"hi"</span><span class="pun">));</span><span class="pln">

    fn takes_long_type</span><span class="pun">(</span><span class="pln">f</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Thunk</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">

    fn returns_long_type</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Thunk</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;">
	[الشيفرة 25: استخدام اسم بديل <code>Thunk</code> لتقليل التكرار]
</p>

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

<p>
	تُستخدم الأسماء البديلة للنوع أيضًا كثيرًا مع نوع <code>&lt;Result&lt;T, E</code> لتقليل التكرار، خذ على سبيل المثال وحدة <code>std::io</code> في المكتبة القياسية، إذ غالبًا ما تُعيد عمليات الدخل والخرج النوع <code>&lt;Result&lt;T, E</code> للتعامل مع المواقف التي تفشل فيها العمليات هذه، وتحتوي هذه المكتبة على هيكل <code>std::io::Error</code> الذي يمثل جميع أخطاء الدخل والخرج المحتملة، وتعيد العديد من الدوال في <code>std::io</code> النوع<code>&lt;Result&lt;T, E</code> بحيث تكون قيمة <code>E</code> هي <code>std::io::Error</code> كما هو الأمر بالنسبة للدوال الموجودة في سمة <code>Write</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_18" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">fmt</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">Error</span><span class="pun">;</span><span class="pln">

pub trait </span><span class="typ">Write</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn write</span><span class="pun">(&amp;</span><span class="pln">mut self</span><span class="pun">,</span><span class="pln"> buf</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;[</span><span class="pln">u8</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">usize</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">&gt;;</span><span class="pln">
    fn flush</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">Result</span><span class="pun">&lt;(),</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">&gt;;</span><span class="pln">

    fn write_all</span><span class="pun">(&amp;</span><span class="pln">mut self</span><span class="pun">,</span><span class="pln"> buf</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;[</span><span class="pln">u8</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">Error</span><span class="pun">&gt;;</span><span class="pln">
    fn write_fmt</span><span class="pun">(&amp;</span><span class="pln">mut self</span><span class="pun">,</span><span class="pln"> fmt</span><span class="pun">:</span><span class="pln"> fmt</span><span class="pun">::</span><span class="typ">Arguments</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">Error</span><span class="pun">&gt;;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تكررت <code>&lt;Result&lt;..., Error</code> كثيرًا، كما احتوت الوحدة <code>std::io</code> على هذا النوع من التصريح:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_20" style=""><span class="pln">type </span><span class="typ">Result</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"> std</span><span class="pun">::</span><span class="pln">result</span><span class="pun">::</span><span class="typ">Result</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">,</span><span class="pln"> std</span><span class="pun">::</span><span class="pln">io</span><span class="pun">::</span><span class="typ">Error</span><span class="pun">&gt;;</span></pre>

<p>
	يمكننا استخدام الاسم البديل المؤهل كليًا <code>&lt;std::io::Result&lt;T</code> لأن هذا التصريح موجود في الوحدة <code>std::io</code>، ويعني النوع السابق وجود النوع <code>&lt;Result&lt;T, E</code> مع ملء <code>E</code> بقيمة <code>std::io::Error</code>. تبدو بصمة السمة <code>Write</code> بنهاية المطاف على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_22" style=""><span class="pln">pub trait </span><span class="typ">Write</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn write</span><span class="pun">(&amp;</span><span class="pln">mut self</span><span class="pun">,</span><span class="pln"> buf</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;[</span><span class="pln">u8</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="str">&lt;usize&gt;</span><span class="pun">;</span><span class="pln">
    fn flush</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">Result</span><span class="pun">&lt;()&gt;;</span><span class="pln">

    fn write_all</span><span class="pun">(&amp;</span><span class="pln">mut self</span><span class="pun">,</span><span class="pln"> buf</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;[</span><span class="pln">u8</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;()&gt;;</span><span class="pln">
    fn write_fmt</span><span class="pun">(&amp;</span><span class="pln">mut self</span><span class="pun">,</span><span class="pln"> fmt</span><span class="pun">:</span><span class="pln"> fmt</span><span class="pun">::</span><span class="typ">Arguments</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;()&gt;;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يساعد الاسم البديل للنوع بطريقتين، فهو يجعل كتابة الشيفرات أسهل ويعطينا واجهةً متسقة عبر جميع أنواع <code>std::io</code>، إذ نظرًا لأن الاسم البديل هو <code>&lt;Result&lt;T, E</code> ببساطة فهذا يعني أننا نستطيع استخدام أي تابع يعمل على <code>&lt;Result&lt;T, E</code> بالإضافة إلى صيغة خاصة مثل العامل <code>?</code>.
</p>

<h3>
	النوع Never الذي لا يعيد أي قيمة
</h3>

<p>
	تمتلك لغة رست نوعًا خاصًا يدعى <code>!</code>، وهذا النوع معروف في لغة نظرية النوع بالنوع الفارغ empty type لأنه لا يحتوي على أي قيم، إلا أننا نفضل أن نطلق عليه اسم ''أبدًا Never'' لأنه يحلّ مكان النوع المُعاد عندما لا تُعيد الدالة أي قيمة، إليك مثالًا على ذلك:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_24" style=""><span class="pln">fn bar</span><span class="pun">()</span><span class="pln"> </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="com">// --snip--</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تُقرأ الشيفرة السابقة على أنّ الدالة <code>bar</code> لا تُعيد أي قيمة، وتسمى الدوال التي لا تُعيد أي قيمة بالدوال المتباينة diverging functions. لا يمكننا إنشاء قيم من النوع <code>!</code> لذلك لا يمكن للدالة <code>bar</code> أن تُعيد أي شيء.
</p>

<p>
	لكن ما فائدة نوع لا يمكنك أبدًا إنشاء قيم له؟ تذكر الشيفرة 5 سابقًا من المقال <a href="https://academy.hsoub.com/programming/rust/%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%BA%D9%8A%D8%B1-%D8%A7%D9%84%D8%A2%D9%85%D9%86%D8%A9-unsafe-rust-r2111/" rel="">لغة رست غير الآمنة Unsafe Rust</a> التي كانت جزءًا من لعبة التخمين بالأرقام، ولنعيد إنتاج جزء منها هنا في الشيفرة 26.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_26" style=""><span class="pln">        let guess</span><span class="pun">:</span><span class="pln"> u32 </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></pre>

<p style="text-align: center;">
	[الشيفرة 26: بنية <code>match</code> بذراع ينتهي بتعليمة <code>continue</code>]
</p>

<p>
	تخطينا بعض التفاصيل في هذه الشيفرة، إذ ناقشنا سابقًا في المقال <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 للتحكم بسير برامج لغة رست</a>، أن أذرع <code>match</code> يجب أن تُعيد جميعها النوع ذاته، وهذا السبب وراء عدم عمل الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_28" style=""><span class="pln">    let guess </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">_</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="lit">5</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="str">"hello"</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">};</span></pre>

<p>
	يجب أن يكون النوع <code>guess</code> في هذه الشيفرة عددًا صحيحًا وسلسلة وتتطلب رست أن يكون <code>guess</code> نوعًا واحدًا فقط. إذًا، ماذا تُعيد <code>continue</code>؟ كيف سُمحَ لنا بإعادة <code>u32</code> من ذراع مع وجود ذراع آخر ينتهي بالتعليمة <code>continue</code> في الشيفرة 26؟
</p>

<p>
	لربّما خمّنت ذلك فعلًا، إذ للتعليمة <code>continue</code> قيمة <code>!</code>، وذلك يعني أنه عندما تحسب رست النوع <code>guess</code> فإنها تنظر إلى ذراعي التطابق، الأول بقيمة <code>u32</code> والأخير بقيمة <code>!</code>، ولأنه ليس من الممكن للقيمة <code>!</code> أن تكون لها قيمة أبدًا، تقرر رست أن النوع <code>guess</code> هو <code>u32</code>.
</p>

<p>
	الطريقة الرسمية لوصف هذا السلوك هي أنه يمكن إجبار التعبيرات من النوع <code>!</code> على أي نوع آخر. يُسمح لنا بإنهاء ذراع <code>match</code> هذا بالكلمة المفتاحية <code>continue</code> لأن <code>continue</code> لا تُعيد قيمة، ولكن بدلًا من ذلك يُنقل عنصر التحكم مرةً أخرى إلى أعلى الحلقة، لذلك في حالة <code>Err</code> لا نعيّن قيمة للمتغير <code>guess</code> إطلاقًا.
</p>

<p>
	النوع "أبدًا never" مفيدٌ في ماكرو <code>!panic</code> أيضًا؛ تذكر دالة <code>unwrap</code> التي نستدعيها على قيم <code>&lt;Option&lt;T</code> لإنتاج قيمة أو هلع بهذا التعريف:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_30" 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</span><span class="pun">(</span><span class="pln">self</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">val</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> val</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"> panic</span><span class="pun">!(</span><span class="str">"called `Option::unwrap()` on a `None` 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="pun">}</span></pre>

<p>
	يحدث أمر مماثل في هذه الشيفرة للشيفرة 26 ضمن <code>match</code>، إذ ترى رست أن <code>val</code> لديه النوع <code>T</code> و <code>!panic</code> من النوع <code>!</code> لذا فإن نتيجة تعبير <code>match</code> الكلي هي <code>T</code>. تعمل هذه الشيفرة لأن <code>!panic</code> لا تنتج قيمة تنهي البرنامج. لن نعيد قيمة من <code>unwrap</code> في حالة <code>None</code> لذا فإن هذه الشيفرة صالحة.
</p>

<p>
	يحتوي تعبير أخير على النوع <code>!</code> ألا وهو الحلقة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_32" style=""><span class="pln">    print</span><span class="pun">!(</span><span class="str">"forever "</span><span class="pun">);</span><span class="pln">

    loop </span><span class="pun">{</span><span class="pln">
        print</span><span class="pun">!(</span><span class="str">"and ever "</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span></pre>

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

<h3>
	الأنواع ذات الحجم الديناميكي والسمة Sized
</h3>

<p>
	تحتاج رست إلى معرفة تفاصيل معينة حول الأنواع المستخدمة، مثل مقدار المساحة المراد تخصيصها لقيمة من نوع معين، وهذا يجعل من أحد جوانب نظام النوع الخاص به مربكًا بعض الشيء في البداية، تحديدًا مفهوم الأنواع ذات الحجم الديناميكي Dynamically Sized Types، ويشار إليها أحيانًا باسم DST أو الأنواع غير محددة الحجم unsized types، إذ تتيح لنا هذه الأنواع كتابة الشيفرات باستخدام قيم لا يمكننا معرفة حجمها إلا وقت التنفيذ.
</p>

<p>
	لنتعمق في تفاصيل النوع ذو الحجم الديناميكي المسمى <code>str</code> الذي استخدمناه سابقًا في جميع أنحاء السلسلة <a href="https://academy.hsoub.com/search/?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&amp;updated_after=any&amp;sortby=newest" rel="">البرمجة بلغة رست</a>، لاحظ أننا لم نقل <code>str&amp;</code> وإنما <code>str</code> بذاتها، إذ تُعدّ من الأنواع ذات الحجم الديناميكي. لا يمكننا معرفة طول السلسلة حتى وقت التنفيذ، مما يعني أنه لا يمكننا إنشاء متغير من النوع <code>str</code>، ولا يمكننا أخذ وسيط من النوع <code>str</code>. ألقِ نظرةً على الشيفرة التالية التي لا تعمل:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_34" style=""><span class="pln">    let s1</span><span class="pun">:</span><span class="pln"> str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Hello there!"</span><span class="pun">;</span><span class="pln">
    let s2</span><span class="pun">:</span><span class="pln"> str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"How's it going?"</span><span class="pun">;</span></pre>

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

<p>
	إذًا ماذا نفعل؟ يجب أن تعلم الإجابة مسبقًا في هذه الحالة، إذ أن الحلّ هو بإنشاء الأنواع <code>s1</code> و <code>s2</code>و <code>str&amp;</code> بدلًا من <code>str</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="">المراجع References والاستعارة Borrowing والشرائح Slices في لغة رست</a> أن هيكل بيانات الشريحة يخزن فقط موضع البداية وطول الشريحة، لذلك على الرغم من أن <code>T&amp;</code> هي قيمة واحدة تخزن عنوان الذاكرة الخاص بالمكان الذي يوجد فيه <code>T</code> إلا أن <code>str&amp;</code> هي قيمتان، ألا وهما عنوان <code>str</code> وطولها، ويمكننا على هذا النحو معرفة حجم قيمة <code>str&amp;</code> في وقت التصريف، وهي ضعف طول <code>usize</code>، أي أننا نعرف دائمًا حجم <code>str&amp;</code> بغض النظر عن طول السلسلة التي تشير إليها. هذه هي الطريقة التي تُستخدم بها الأنواع ذات الحجم الديناميكي عمومًا في رست، إذ لهذه الأنواع مقدار إضافي من البيانات الوصفية metadata التي تخزن حجم المعلومات الديناميكية. القاعدة الذهبية للأنواع ذات الحجم الديناميكي هي أنه يجب علينا دائمًا وضع قيم للأنواع ذات الحجم الديناميكي خلف مؤشر من نوع ما.
</p>

<p>
	يمكننا دمج <code>str</code> مع جميع أنواع المؤشرات، على سبيل المثال <code>&lt;Box&lt;str</code> أو <code>&lt;Rc&lt;str</code>، وقد فعلنا ذلك سابقًا ولكن بنوع ذو حجم ديناميكي مختلف، ألا وهو السمات traits، فكل سمة هي نوع ذو حجم ديناميكي يمكننا الرجوع إليه باستخدام اسم السمة. ذكرنا سابقًا في المقال <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%B3%D9%85%D8%A9-object-trait-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2098/" rel="">استخدام كائنات السمة Object Trait في لغة رست</a> أنه يجب وضع السمات خلف مؤشر لاستخدامها مثل كائنات سمات، مثل <code>dyn Trait&amp;</code> أو <code>&lt;Box&lt;dyn Trait</code> (يمكن استخدام <code>&lt;Rc&lt;dyn Trait</code> أيضًا).
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_36" style=""><span class="pln">fn </span><span class="kwd">generic</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;(</span><span class="pln">t</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="com">// --snip--</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	كما لو أننا كتبنا هذا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_38" style=""><span class="pln">fn </span><span class="kwd">generic</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Sized</span><span class="pun">&gt;(</span><span class="pln">t</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="com">// --snip--</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_40" style=""><span class="pln">fn </span><span class="kwd">generic</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">:</span><span class="pln"> </span><span class="pun">?</span><span class="typ">Sized</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"> </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>Sized?</code> تعني أن "<code>T</code> قد تكون أو لا تكون <code>Sized</code>" وهذا الترميز يلغي الافتراض الذي ينص على وجود حجم معروف للأنواع العامة وقت التصريف. صيغة <code>Trait?</code> بهذا المعنى متاحة فقط للسمة <code>Sized</code> وليس لأي سمات أخرى.
</p>

<p>
	لاحظ أيضًا أننا بدّلنا نوع المعامل <code>t</code> من <code>T</code> إلى <code>T&amp;</code>، نظرًا لأن النوع قد لا يكون <code>Sized</code> فنحن بحاجة إلى استخدامه خلف نوع من المؤشرات، وفي هذه الحالة اخترنا مرجعًا.
</p>

<h2>
	الدوال functions والمغلفات closures المتقدمة
</h2>

<p>
	حان الوقت للتحدث عن بعض الخصائص المتقدمة المتعلقة بالمغلّفات والدوال بما في ذلك مؤشرات الدوال والمغلفات الراجعة Returing Closures.
</p>

<h3>
	مؤشرات الدوال
</h3>

<p>
	تحدثنا سابقًا عن كيفية تمرير المغلفات للدوال، ويمكننا أيضًا تمرير الدوال العادية للدوال. تفيد هذه التقنية عندما نريد تمرير دالة عرّفناها مسبقًا بدلًا من تعريف مغلف جديد. تُجبَر الدوال بالنوع <code>fn</code> (بحرف f صغير) -لا تخلط بينه وبين مغلف السمة <code>Fn</code>- يسمى نوع <code>fn</code> مؤشر دالة function pointer، ويسمح لك تمرير الدوال بمؤشرات الدوال باستخدام الدوال مثل وسطاء لدوال أُخرى.
</p>

<p>
	تشابه صياغة مؤشرات الدوال لتحديد معامل مثل مؤشر صياغتها في المغلفات كما تبين الشيفرة 27، إذ عرّفنا تابع <code>add_one</code> الذي يضيف واحد إلى معامله. تأخذ الدالة <code>do_twice</code> معاملين، هما مؤشر دالة لأي دالة تأخذ معامل <code>i32</code> وتعيد النوع <code>i32</code>، وقيمة <code>i32</code> واحدة. تستدعي دالة <code>do_twice</code> الدالة <code>f</code> مرتين وتمرر قيمة <code>arg</code> وتضيف نتيجتَي استدعاء الدالة معًا، بينما تستدعي الدالة <code>main</code> الدالة <code>do_twice</code> مع الوسيطين <code>add_one</code> و 5.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_42" style=""><span class="pln">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">

fn do_twice</span><span class="pun">(</span><span class="pln">f</span><span class="pun">:</span><span class="pln"> fn</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"> arg</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">
    f</span><span class="pun">(</span><span class="pln">arg</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">arg</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 answer </span><span class="pun">=</span><span class="pln"> do_twice</span><span class="pun">(</span><span class="pln">add_one</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">);</span><span class="pln">

    println</span><span class="pun">!(</span><span class="str">"The answer is: {}"</span><span class="pun">,</span><span class="pln"> answer</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 27: استخدام نوع <code>fn</code> لقبول مؤشر دالة مثل وسيط]
</p>

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

<pre class="ipsCode">The answer is: 12
</pre>

<p>
	حددنا أن المعامل <code>f</code> في <code>do_twice</code> هو <code>fn</code> الذي يأخذ معامل واحد من النوع <code>i32</code> ويُعيد <code>i32</code>، ويمكن بعدها استدعاء <code>f</code> من داخل الدالة <code>do_twice</code>. يمكننا في <code>main</code> تمرير اسم الدالة <code>add_one</code> على أنه الوسيط الأول إلى <code>do_twice</code>.
</p>

<p>
	على عكس المغلفات، فإن <code>fn</code> هو نوع وليس سمة، لذا نحدد <code>fn</code> مثل نوع معامل مباشرة بدلًا من تصريح معامل نوع معمم generic مع واحدة من سمات <code>fn</code> على أنه قيد سمة trait bound.
</p>

<p>
	تطبّق مؤشرات الدالة سمات المغلفة الثلاثة (<code>Fn</code> و <code>FnMut</code> و <code>FnOnce</code>). يعني ذلك أنه بإمكانك دائمًا تمرير مؤشر الدالة مثل وسيط لدالة تتوقع مغلفًا. هذه هي الطريقة الأفضل لكتابة الدوال باستخدام النوع المعمم وواحد من مغلف السمات بحيث يمكن للدوال الأخرى قبول دوال أو مغلفات. هناك مثال واحد تستطيع فيه قبول <code>fn</code> فقط وليس المغلفات وهو عندما نتعامل مع شيفرة خارجية لا تحتوي على مغلفات. يمكن لدوال لغة البرمجة سي أن تقبل الدوال مثل وسطاء، لكن ليس لديها مغلفات.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_44" style=""><span class="pln">  let list_of_numbers </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 list_of_strings</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">
        list_of_numbers</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">i</span><span class="pun">|</span><span class="pln"> i</span><span class="pun">.</span><span class="pln">to_string</span><span class="pun">()).</span><span class="pln">collect</span><span class="pun">();</span></pre>

<p>
	أو يتسمية التابع مثل وسيط <code>map</code> بدلًا من المغلف على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_46" style=""><span class="pln">  let list_of_numbers </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 list_of_strings</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">
        list_of_numbers</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="typ">ToString</span><span class="pun">::</span><span class="pln">to_string</span><span class="pun">).</span><span class="pln">collect</span><span class="pun">();</span></pre>

<p>
	لاحظ أنه يجب استخدام الصيغة المؤهلة كليًاالتي تحدثنا عنها سابقًا في قسم "السمات المتقدمة" لأنه يوجد دوال متعددة جاهزة اسمها <code>to_string</code>. استخدمنا هنا الدالة <code>to_string</code> المعرّفة في سمة <code>ToString</code> التي تطبّقها المكتبة القياسية لأي نوع يطبّق <code>Display</code>.
</p>

<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="">التعدادات enums في لغة رست</a> أن اسم كل متغاير variant في تعداد enum عرّفناه يصبح أيضًا دالة تهيئة. يمكننا استخدام دوال التهيئة هذه مثل مؤشرات دالة تطبّق مغلفات السمة، ما يعني أنه يمكننا تحديد دوال التهيئة مثل وسطاء للتوابع التي تقبل المغلفات على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_48" style=""><span class="pln">  </span><span class="kwd">enum</span><span class="pln"> </span><span class="typ">Status</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Value</span><span class="pun">(</span><span class="pln">u32</span><span class="pun">),</span><span class="pln">
        </span><span class="typ">Stop</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    let list_of_statuses</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">Status</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="lit">0u32.</span><span class="pun">.</span><span class="lit">20</span><span class="pun">).</span><span class="typ">map</span><span class="pun">(</span><span class="typ">Status</span><span class="pun">::</span><span class="typ">Value</span><span class="pun">).</span><span class="pln">collect</span><span class="pun">();</span></pre>

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

<h3>
	إعادة المغلفات
</h3>

<p>
	تُمثل المغلفات بسمات، ما يعني أنه لا يمكن إعادة المغلفات مباشرةً، إذ يمكنك استخدام النوع الحقيقي الذي ينفذ السمة مثل قيمة معادة للدالة في معظم الحالات عندما تريد إعادة سمة بدلًا من ذلك، ولكن لا يمكنك فعل ذلك في المغلفات لأنها لا تحتوي نوعًا حقيقيًا يمكن إعادته. على سبيل المثال، يُمنع استخدام مؤشرات الدالة <code>fn</code> مثل نوع مُعاد.
</p>

<p>
	تحاول الشيفرة التالية إعادة مغلف مباشرةً، ولكنها لن تُصرّف.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_50" style=""><span class="pln">fn returns_closure</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> dyn </span><span class="typ">Fn</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">
    </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>
	يكون خطأ المصرّف على النحو التالي:
</p>

<pre class="ipsCode">$ cargo build
   Compiling functions-example v0.1.0 (file:///projects/functions-example)
error[E0746]: return type cannot have an unboxed trait object
 --&gt; src/lib.rs:1:25
  |
1 | fn returns_closure() -&gt; dyn Fn(i32) -&gt; i32 {
  |                         ^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
  |
  = note: for information on `impl Trait`, see &lt;https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits&gt;
help: use `impl Fn(i32) -&gt; i32` as the return type, as all return paths are of type `[closure@src/lib.rs:2:5: 2:8]`, which implements `Fn(i32) -&gt; i32`
  |
1 | fn returns_closure() -&gt; impl Fn(i32) -&gt; i32 {
  |                         ~~~~~~~~~~~~~~~~~~~

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3388_52" style=""><span class="pln">fn returns_closure</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">Fn</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">&gt;</span><span class="pln"> </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="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">
</span><span class="pun">}</span></pre>

<p>
	تُصرّف الشيفرة بصورةٍ اعتيادية هنا. راجع المقال <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%B3%D9%85%D8%A9-object-trait-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2098/" rel="">استخدام كائنات السمة Object Trait في لغة رست</a>. سنتحدث لاحقًا عن الماكرو.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch19-00-advanced-features.html" rel="external nofollow">Advanced Features</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%A7%D9%83%D8%B1%D9%88-macros-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2149/" rel="">الماكرو Macros في لغة رست</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D8%B9%D9%86-%D8%A7%D9%84%D8%B3%D9%85%D8%A7%D8%AA-trait-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2112/" rel="">مفاهيم متقدمة عن السمات Trait في لغة رست</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D9%86%D9%85%D8%B7-%D8%AA%D8%B5%D9%85%D9%8A%D9%85%D9%8A-design-pattern-%D9%83%D8%A7%D8%A6%D9%86%D9%8A-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-object-oriented-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2108/" rel="">تنفيذ نمط تصميمي Design Pattern كائني التوجه Object-Oriented في لغة رست</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">2148</guid><pubDate>Thu, 05 Oct 2023 13:00:00 +0000</pubDate></item><item><title>&#x645;&#x641;&#x627;&#x647;&#x64A;&#x645; &#x645;&#x62A;&#x642;&#x62F;&#x645;&#x629; &#x639;&#x646; &#x627;&#x644;&#x633;&#x645;&#x627;&#x62A; Trait &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A;</title><link>https://academy.hsoub.com/programming/rust/%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D8%B9%D9%86-%D8%A7%D9%84%D8%B3%D9%85%D8%A7%D8%AA-trait-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2112/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_09/-Trait-----Rust.png.3b35b9a816e5dc86082958d6f13ce076.png" /></p>
<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="">السمات Traits في لغة رست Rust</a>، إلا أننا لم نناقش التفاصيل الأكثر تقدمًا. سنخوض الآن بالتفاصيل الجوهرية بعد أن تعملت المزيد عن لغة رست حتى الآن.
</p>

<h2>
	تحديد أنواع الموضع المؤقت في تعريفات السمات مع الأنواع المرتبطة
</h2>

<p>
	تصل الأنواع المرتبطة associated types نوع موضع مؤقت placeholder بسمة بحيث يمكن لتعريفات تابع السمة استخدام أنواع المواضع المؤقتة هذه في بصماتها signature، وسيحدد منفّذ السمة النوع الحقيقي الذي سيُستخدم بدلًا من نوع الموضع المؤقت للتنفيذ المعين. يمكننا بهذه الطريقة تحديد سمة تستخدم بعض الأنواع دون الحاجة إلى معرفة ماهية هذه الأنواع تحديدًا حتى تُطبَّق السمة.
</p>

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

<p>
	أحد الأمثلة على سمة ذات نوع مرتبط هي سمة <code>Iterator</code> التي توفرها المكتبة القياسية. يُطلق على النوع المرتبط اسم <code>Item</code> ويرمز إلى نوع القيم التي يمرّ عليها النوع الذي ينفّذ سمة <code>Iterator</code>. تُعرّف سمة <code>Iterator</code> كما هو موضح في الشيفرة 12.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7266_6" 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="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 12: تعريف سمة <code>Iterator</code> التي لديها نوع مرتبط <code>Item</code>]
</p>

<p>
	النوع <code>Item</code> هو موضع مؤقت، ويوضح تعريف تابع <code>next</code> أنه سيعيد قيمًا من النوع <code>&lt;Option&lt;Self::Item</code>. سيحدد منفّذ سمة <code>Iterator</code> النوع الحقيقي للنوع <code>Item</code> وسيُعيد التابع <code>next</code> النوع <code>Option</code> الذي يحتوي على قيمة من هذا النوع الحقيقي.
</p>

<p>
	قد تبدو الأنواع المرتبطة مثل مفهوم مشابه للأنواع المعممة من حيث أن الأخير يسمح لنا بتعريف دالة دون تحديد الأنواع التي يمكنها التعامل معها، ولفحص الاختلاف بين المفهومين، سنلقي نظرةً على تنفيذ سمة <code>Iterator</code> على نوع يسمى <code>Counter</code> الذي يحدد نوع <code>Item</code> هو <code>u32</code>:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7266_8" style=""><span class="pln">impl </span><span class="typ">Iterator</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">Counter</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    type </span><span class="typ">Item</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> u32</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="pun">{</span><span class="pln">
        </span><span class="com">// --snip--</span></pre>

<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="">الأنواع المعمّمة generic</a>، فلماذا لا نكتفي بتعريف سمة <code>Iterator</code> باستخدام الأنواع المعممة كما هو موضح في الشيفرة 13؟
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7266_10" style=""><span class="pln">pub trait </span><span class="typ">Iterator</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 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="pln">T</span><span class="pun">&gt;;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 13: تعريف افتراضي لسمة <code>Iterator</code> باستخدام الأنواع المعممة]
</p>

<p>
	الفرق هو أنه علينا أن نوضح الأنواع في كل تنفيذ عند استخدام الأنواع المعمّمة كما في الشيفرة 13، لأنه يمكننا أيضًا تنفيذ <code>Iterator&lt;String&gt; for Counter</code> أو أي نوع آخر، فقد يكون لدينا تنفيذات متعددة للسمة <code>Iterator</code> من أجل <code>Counter</code>. بعبارة أخرى: عندما تحتوي السمة على معامل معمّم، يمكن تنفيذه لنوع ما عدة مرات مع تغيير الأنواع الحقيقية لمعاملات النوع المعمم في كل مرة، وعندما نستخدم التابع <code>next</code> على <code>Counter</code> سيتوجب علينا توفير التعليقات التوضيحية من النوع للإشارة إلى تنفيذ <code>Iterator</code> الذي نريد استخدامه.
</p>

<p>
	لا نحتاج مع الأنواع المرتبطة إلى إضافة تعليقات توضيحية للأنواع، لأننا لا نستطيع تنفيذ سمة على نوع عدة مرات. يمكننا فقط اختيار نوع <code>Item</code> مرةً واحدةً في الشيفرة 12 مع التعريف الذي يستخدم الأنواع المرتبطة لأنه لا يمكن أن يكون هناك سوى <code>impl Iterator for Counter</code> واحد. لا يتعين علينا تحديد أننا نريد مكررًا لقيم <code>u32</code> في كل مكان نستدعي <code>next</code> على <code>Counter</code>.
</p>

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

<h2>
	معاملات النوع المعمم الافتراضي وزيادة تحميل العامل
</h2>

<p>
	عندما نستخدم معاملات النوع المعمم يمكننا تحديد نوع حقيقي افتراضي للنوع المعمم، وهذا يلغي الحاجة إلى منفّذي السمة لتحديد نوع حقيقي إذا كان النوع الافتراضي يعمل. يمكنك تحديد نوع افتراضي عند التصريح عن نوع عام باستخدام الصيغة <code>&lt;PlaceholderType=ConcreteType&gt;</code>.
</p>

<p>
	من الأمثلة الرائعة على الموقف الذي تكون فيه هذه التقنية مفيدة هو زيادة تحميل العامل operator overloading، إذ يمكنك تخصيص سلوك عامل (مثل <code>+</code>) في مواقف معينة.
</p>

<p>
	لا تسمح لك رست بإنشاء عواملك الخاصة أو زيادة تحميل العوامل العشوائية، ولكن يمكنك زيادة تحميل العمليات والسمات المقابلة المدرجة في <code>std::ops</code> من خلال تنفيذ السمات المرتبطة بالعامل. على سبيل المثال في الشيفرة 14 زدنا تحميل العامل <code>+</code> لإضافة نسختين <code>Point</code> معًا عن طريق تنفيذ سمة <code>Add</code> على بنية <code>Point</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7266_12" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">ops</span><span class="pun">::</span><span class="typ">Add</span><span class="pun">;</span><span class="pln">

</span><span class="com">#[derive(Debug, Copy, Clone, PartialEq)]</span><span class="pln">
</span><span class="kwd">struct</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"> 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="pun">}</span><span class="pln">

impl </span><span class="typ">Add</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">Point</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    type </span><span class="typ">Output</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Point</span><span class="pun">;</span><span class="pln">

    fn add</span><span class="pun">(</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">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Point</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"> other</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"> self</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">
    assert_eq</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</span><span class="pun">,</span><span class="pln"> y</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"> </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">2</span><span class="pun">,</span><span class="pln"> y</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="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">3</span><span class="pun">,</span><span class="pln"> y</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><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 14: تنفيذ سمة <code>Add</code> لزيادة تحميل العامل <code>+</code> لنسخ <code>Point</code>]
</p>

<p>
	يضيف التابع <code>add</code> قيم <code>x</code> لنسختَي <code>Point</code> وقيم <code>y</code> لنسختَي <code>Point</code> لإنشاء <code>Point</code> جديدة. للسمة <code>Add</code> نوع مرتبط يسمى <code>Output</code> الذي يحدد النوع الذي يُعاد من التابع <code>add</code>.
</p>

<p>
	النوع المعمم الافتراضي في هذه الشيفرة موجودٌ ضمن سمة <code>Add</code>. إليك تعريفه:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7266_14" style=""><span class="pln">trait </span><span class="typ">Add</span><span class="pun">&lt;</span><span class="typ">Rhs</span><span class="pun">=</span><span class="typ">Self</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    type </span><span class="typ">Output</span><span class="pun">;</span><span class="pln">

    fn add</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> rhs</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Rhs</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="pun">::</span><span class="typ">Output</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يجب أن تبدو هذه الشيفرة مألوفة عمومًا: سمة ذات تابع واحد ونوع مرتبط بها. الجزء الجديد هو <code>Rhs=Self</code>، إذ يُطلق على الصيغة هذه معاملات النوع الافتراضية default type parameters. يعرّف معامل النوع المعمم <code>Rhs</code> (اختصار للجانب الأيمن right hand size) نوع المعامل <code>rhs</code> في تابع <code>add</code>. إذا لم نحدد نوعًا حقيقيًا للقيمة <code>Rhs</code> عند تنفيذ سمة <code>Add</code>، سيُعيّن نوع <code>Rhs</code> افتراضيًا إلى <code>Self</code> الذي سيكون النوع الذي ننفّذ السمة <code>Add</code> عليه.
</p>

<p>
	عندما نفّذنا <code>Add</code> على <code>Point</code> استخدمنا الإعداد الافتراضي للنوع <code>Rhs</code> لأننا أردنا إضافة نسختين <code>Point</code>. لنلقي نظرةً على مثال لتنفيذ سمة <code>Add</code>، إذ نريد تخصيص نوع <code>Rhs</code> بدلًا من الاستخدام الافتراضي.
</p>

<p>
	لدينا الهيكلان <code>Millimeters</code> و <code>Meters</code> اللذان يحملان قيمًا في وحدات مختلفة، يُعرف هذا الغلاف الرقيق لنوع موجود في هيكل آخر باسم نمط النوع الجديد newtype pattern الذي نصفه بمزيد من التفصيل لاحقًا في قسم "استخدام نمط النوع الجديد لتنفيذ السمات الخارجية على الأنواع الخارجية". نريد أن نضيف القيم بالمليمترات إلى القيم بالأمتار وأن نجعل تنفيذ <code>Add</code> يجري التحويل صحيحًا. يمكننا تنفيذ <code>Add</code> للنوع <code>Millimeters</code> مع <code>Meters</code> مثل <code>Rhs</code> كما هو موضح في الشيفرة 15.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7266_16" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">ops</span><span class="pun">::</span><span class="typ">Add</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Millimeters</span><span class="pun">(</span><span class="pln">u32</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Meters</span><span class="pun">(</span><span class="pln">u32</span><span class="pun">);</span><span class="pln">

impl </span><span class="typ">Add</span><span class="pun">&lt;</span><span class="typ">Meters</span><span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">Millimeters</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    type </span><span class="typ">Output</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Millimeters</span><span class="pun">;</span><span class="pln">

    fn add</span><span class="pun">(</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">Meters</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Millimeters</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Millimeters</span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="lit">0</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">other</span><span class="pun">.</span><span class="lit">0</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">1000</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>Add</code> على <code>Millimeters</code> لإضافة <code>Millimeters</code> للنوع <code>Meters</code>]
</p>

<p>
	لإضافة <code>Millimeters</code> و <code>Meters</code> نحدد <code>&lt;impl Add&lt;Meters</code> لتعيين قيمة محدد نوع <code>Rhs</code> بدلًا من استخدام الافتراضي <code>Self</code>.
</p>

<p>
	ستستخدم معاملات النوع الافتراضية بطريقتين رئيسيتين:
</p>

<ul>
	<li>
		لتوسيع نوع دون تعطيل الشيفرة الموجودة.
	</li>
	<li>
		للسماح بالتخصيص في حالات معينة لن يحتاجها معظم المستخدمين.
	</li>
</ul>

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

<h3>
	صيغة مؤهلة كليا للتوضيح باستدعاء التوابع التي تحمل الاسم ذاته
</h3>

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

<p>
	عند استدعاء التوابع التي تحمل الاسم نفسه، ستحتاج إلى إخبار رست بالتابع الذي تريد استخدامه. ألقِ نظرةً على الشيفرة 16، إذ عرّفنا سمتين <code>Pilot</code> و <code>Wizard</code> ولكل منهما تابع يسمى <code>fly</code>، ثم نفّذنا كلتا السمتين على نوع <code>Human</code> لديه فعلًا تابع يسمى <code>fly</code> منفّذ عليه، وكل تابع <code>fly</code> يفعل شيئًا مختلفًا.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7266_18" style=""><span class="pln">trait </span><span class="typ">Pilot</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn fly</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">

trait </span><span class="typ">Wizard</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn fly</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">struct</span><span class="pln"> </span><span class="typ">Human</span><span class="pun">;</span><span class="pln">

impl </span><span class="typ">Pilot</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">Human</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn fly</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">
        println</span><span class="pun">!(</span><span class="str">"This is your captain speaking."</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="typ">Wizard</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">Human</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn fly</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">
        println</span><span class="pun">!(</span><span class="str">"Up!"</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="typ">Human</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn fly</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">
        println</span><span class="pun">!(</span><span class="str">"*waving arms furiously*"</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;">
	[الشيفرة 16: تعريف سمتين بحيث تملكان تابع <code>fly</code> وتنفيذهما على النوع <code>Human</code> وتنفيذ تابع <code>fly</code> على <code>Human</code> مباشرةً]
</p>

<p>
	عندما نستدعي <code>fly</code> على نسخة <code>Human</code> يتخلف المصرف عن استدعاء التابع الذي يُنفّذ مباشرةً على النوع كما هو موضح في الشيفرة 17.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7266_20" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let person </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Human</span><span class="pun">;</span><span class="pln">
    person</span><span class="pun">.</span><span class="pln">fly</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 17: استدعاء <code>fly</code> على نسخة <code>Human</code>]
</p>

<p>
	سيؤدي تنفيذ هذه الشيفرة إلى طباعة <code>*waving arms furiously*</code> مما يدل على أن رست استدعت تابع <code>fly</code> المنفّذ على <code>Human</code> مباشرةً.
</p>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7266_22" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let person </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Human</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">Pilot</span><span class="pun">::</span><span class="pln">fly</span><span class="pun">(&amp;</span><span class="pln">person</span><span class="pun">);</span><span class="pln">
    </span><span class="typ">Wizard</span><span class="pun">::</span><span class="pln">fly</span><span class="pun">(&amp;</span><span class="pln">person</span><span class="pun">);</span><span class="pln">
    person</span><span class="pun">.</span><span class="pln">fly</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 18: تحديد تابع السمة <code>fly</code> التي نريد استدعاءه]
</p>

<p>
	يوضح تحديد اسم السمة قبل اسم التابع لرست أي تنفيذ للتابع <code>fly</code> نريد استدعاءه. يمكننا أيضًا كتابة <code>Human::fly(&amp;person)‎</code> وهو ما يعادل <code>person.fly()‎</code>الذي استخدمناه في الشيفرة 18، ولكن هذا أطول قليلًا للكتابة إذا لم نكن بحاجة إلى توضيح.
</p>

<p>
	يؤدي تنفيذ هذه الشيفرة إلى طباعة التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7266_24" style=""><span class="pln">$ cargo run
   </span><span class="typ">Compiling</span><span class="pln"> traits</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/traits-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.46s</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">traits</span><span class="pun">-</span><span class="pln">example</span><span class="pun">`</span><span class="pln">
</span><span class="typ">This</span><span class="pln"> is your captain speaking</span><span class="pun">.</span><span class="pln">
</span><span class="typ">Up</span><span class="pun">!</span><span class="pln">
</span><span class="pun">*</span><span class="pln">waving arms furiously</span><span class="pun">*</span></pre>

<p>
	إذا كان لدينا نوعان ينفذ كلاهما سمةً واحدةً، يمكن أن تكتشف رست أي تنفيذ للسمة يجب استخدامه بناءً على نوع <code>self</code> نظرًا لأن تابع <code>fly</code> يأخذ معامل <code>self</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="">الدوال functions</a> المرتبطة التي ليست بتوابع لا تحتوي على معامل <code>self</code>. عندما تكون هناك أنواع أو سمات متعددة تحدد دوالًا غير تابعية non-method بنفس اسم الدالة، لا تعرف رست دائمًا النوع الذي تقصده ما لم تستخدم صيغة مؤهلة كليًا fully qualified syntax. على سبيل المثال في الشيفرة 19 أنشأنا سمة لمأوى للحيوانات يسمّي جميع الجراء puppies الصغار Spot. ننشئ سمة <code>Animal</code> بدالة غير تابعية مرتبطة <code>baby_name</code>، تُنفّذ <code>Animal</code> لهيكل <code>Dog</code> الذي نوفّر عليه أيضًا دالةً غير تابعية مرتبطة <code>baby_name</code> مباشرةً.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7266_26" style=""><span class="pln">trait </span><span class="typ">Animal</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn baby_name</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><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Dog</span><span class="pun">;</span><span class="pln">

impl </span><span class="typ">Dog</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn baby_name</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">"Spot"</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="typ">Animal</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">Dog</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn baby_name</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">"puppy"</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">
    println</span><span class="pun">!(</span><span class="str">"A baby dog is called a {}"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Dog</span><span class="pun">::</span><span class="pln">baby_name</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 19: سمة لها دالة مرتبطة ونوع له دالة مرتبطة بالاسم ذاته الذي ينفّذ السمة أيضًا]
</p>

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

<p>
	نستدعي في الدالة <code>main</code> الدالة <code>Dog::baby_name</code> التي تستدعي الدالة المرتبطة المعرفة في <code>Dog</code> مباشرةً. تطبع هذه الشيفرة ما يلي:
</p>

<pre class="ipsCode">$ cargo run
   Compiling traits-example v0.1.0 (file:///projects/traits-example)
    Finished dev [unoptimized + debuginfo] target(s) in 0.54s
     Running `target/debug/traits-example`
A baby dog is called a Spot
</pre>

<p>
	لم نكن نتوقع هذه النتيجة، إذ نريد استدعاء دالة <code>baby_name</code> التي تعد جزءًا من سمة <code>Animal</code> التي نفّذناها على <code>Dog</code> حتى تطبع الشيفرة <code>A baby dog is called a puppy</code>. لا تساعد تقنية تحديد اسم السمة التي استخدمناها في الشيفرة 18 هنا، وإذا غيرنا <code>main</code> للشيفرة لما هو موجود في الشيفرة 20، سنحصل على خطأ عند التصريف.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7266_29" style=""><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">"A baby dog is called a {}"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Animal</span><span class="pun">::</span><span class="pln">baby_name</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 20: محاولة استدعاء الدالة <code>baby_name</code> من السمة <code>Animal</code> دون معرفة رست بأي تنفيذ ينبغي استخدامه]
</p>

<p>
	لا تستطيع رست معرفة أي تنفيذ نريده للقيمة <code>Animal::baby_name</code> لأن <code>Animal::baby_name</code> لا تحتوي على معامل <code>self</code> ولأنه يمكن أن تكون هناك أنواع أخرى تنفّذ سمة <code>Animal</code>. سنحصل على خطأ المصرف هذا:
</p>

<pre class="ipsCode">$ cargo run
   Compiling traits-example v0.1.0 (file:///projects/traits-example)
error[E0790]: cannot call associated function on trait without specifying the corresponding `impl` type
  --&gt; src/main.rs:20:43
   |
2  |     fn baby_name() -&gt; String;
   |     ------------------------- `Animal::baby_name` defined here
...
20 |     println!("A baby dog is called a {}", Animal::baby_name());
   |                                           ^^^^^^^^^^^^^^^^^ cannot call associated function of trait
   |
help: use the fully-qualified path to the only available implementation
   |
20 |     println!("A baby dog is called a {}", &lt;Dog as Animal&gt;::baby_name());
   |                                           +++++++       +

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

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7266_32" style=""><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">"A baby dog is called a {}"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&lt;</span><span class="typ">Dog</span><span class="pln"> as </span><span class="typ">Animal</span><span class="pun">&gt;::</span><span class="pln">baby_name</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 21: استعمال صيغة مؤهلة كليًا لتحديد أننا نريد استدعاء دالة <code>baby_name</code> من السمة <code>Animal</code> كما هو منفّذ في <code>Dog</code>]
</p>

<p>
	نقدم لرست تعليقًا توضيحيًا للنوع ضمن أقواس مثلثية angle brackets مما يشير إلى أننا نريد استدعاء تابع <code>baby_name</code> من سمة <code>Animal</code> كما هو منفّذ في <code>Dog</code> بالقول إننا نريد معاملة النوع <code>Dog</code> مثل النوع <code>Animal</code> لاستدعاء الدالة هذه. ستطبع الشيفرة البرمجية الآن ما نريد:
</p>

<pre class="ipsCode">$ cargo run
   Compiling traits-example v0.1.0 (file:///projects/traits-example)
    Finished dev [unoptimized + debuginfo] target(s) in 0.48s
     Running `target/debug/traits-example`
A baby dog is called a puppy
</pre>

<p>
	تعرف الصيغة المؤهلة كليًا عمومًا على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7266_34" style=""><span class="pun">&lt;</span><span class="typ">Type</span><span class="pln"> as </span><span class="typ">Trait</span><span class="pun">&gt;::</span><span class="pln">function</span><span class="pun">(</span><span class="pln">receiver_if_method</span><span class="pun">,</span><span class="pln"> next_arg</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...);</span></pre>

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

<h2>
	استخدام سمات خارقة supertrait لطلب وظيفة إحدى السمات ضمن سمة أخرى
</h2>

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

<p>
	على سبيل المثال لنفترض أننا نريد إنشاء سمة <code>OutlinePrint</code> باستخدام تابع <code>outline_print</code> الذي سيطبع قيمة معينة منسقة بحيث تكون مؤطرة بعلامات نجمية. بالنظر إلى هيكل <code>Point</code> الذي ينفّذ سمة المكتبة القياسية <code>Display</code> لتعطي النتيجة <code>(x, y)</code>، عندما نستدعي <code>outline_print</code> على نسخة <code>Point</code> التي تحتوي على 1 للقيمة <code>x</code> و 3 للقيمة <code>y</code>، يجب أن يطبع ما يلي:
</p>

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

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

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

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

trait </span><span class="typ">OutlinePrint</span><span class="pun">:</span><span class="pln"> fmt</span><span class="pun">::</span><span class="typ">Display</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn outline_print</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">
        let output </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">to_string</span><span class="pun">();</span><span class="pln">
        let len </span><span class="pun">=</span><span class="pln"> output</span><span class="pun">.</span><span class="pln">len</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="str">"*"</span><span class="pun">.</span><span class="pln">repeat</span><span class="pun">(</span><span class="pln">len </span><span class="pun">+</span><span class="pln"> </span><span class="lit">4</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="str">" "</span><span class="pun">.</span><span class="pln">repeat</span><span class="pun">(</span><span class="pln">len </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</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"> output</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="str">" "</span><span class="pun">.</span><span class="pln">repeat</span><span class="pun">(</span><span class="pln">len </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</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="str">"*"</span><span class="pun">.</span><span class="pln">repeat</span><span class="pun">(</span><span class="pln">len </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;">
	[الشيفرة 22: تنفيذ سمة <code>OutlinePrint</code> التي تتطلب الوظيفة من <code>Display</code>]
</p>

<p>
	بما أننا حددنا أن <code>OutlinePrint</code> تتطلب سمة <code>Display</code>، يمكننا استخدام دالة <code>to_string</code> التي تُنفّذ تلقائيًا لأي نوع ينفّذ <code>Display</code>. إذا حاولنا استخدام <code>to_string</code> دون إضافة نقطتين وتحديد سمة <code>Display</code> بعد اسم السمة، سنحصل على خطأ يقول بأنه لم يُعثر على تابع باسم <code>to_string</code> للنوع <code>Self&amp;</code> في النطاق الحالي.
</p>

<p>
	لنرى ما يحدث عندما نحاول تنفيذ <code>OutlinePrint</code> على نوع لا ينفّذ <code>Display</code> مثل هيكل <code>Point</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7266_39" style=""><span class="kwd">struct</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"> 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="pun">}</span><span class="pln">

impl </span><span class="typ">OutlinePrint</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">Point</span><span class="pln"> </span><span class="pun">{}</span></pre>

<p>
	نحصل على خطأ يقول أن <code>Display</code> مطلوبة ولكن غير منفّذة:
</p>

<pre class="ipsCode">$ cargo run
   Compiling traits-example v0.1.0 (file:///projects/traits-example)
error[E0277]: `Point` doesn't implement `std::fmt::Display`
  --&gt; src/main.rs:20:6
   |
20 | impl OutlinePrint for Point {}
   |      ^^^^^^^^^^^^ `Point` cannot be formatted with the default formatter
   |
   = help: the trait `std::fmt::Display` is not implemented for `Point`
   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
note: required by a bound in `OutlinePrint`
  --&gt; src/main.rs:3:21
   |
3  | trait OutlinePrint: fmt::Display {
   |                     ^^^^^^^^^^^^ required by this bound in `OutlinePrint`

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

<p>
	لإصلاح هذا الأمر ننفّذ <code>Display</code> على <code>Point</code> ونلبي القيد الذي تتطلبه <code>OutlinePrint</code> مثل:
</p>

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

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

impl fmt</span><span class="pun">::</span><span class="typ">Display</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">Point</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn fmt</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> f</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">mut fmt</span><span class="pun">::</span><span class="typ">Formatter</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> fmt</span><span class="pun">::</span><span class="typ">Result</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        write</span><span class="pun">!(</span><span class="pln">f</span><span class="pun">,</span><span class="pln"> </span><span class="str">"({}, {})"</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"> 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></pre>

<p>
	سيُصرَّف تنفيذ سمة <code>OutlinePrint</code> على <code>Point</code> بعدها بنجاح، ويمكننا استدعاء <code>outline_print</code> على نسخة <code>Point</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="">السمات Traits في لغة رست</a> في قسم "تطبيق السمة على نوع" القاعدة الوحيدة التي تنص على أنه لا يُسمح لنا إلا بتنفيذ سمة على نوع ما إذا كانت السمة أو النوع محليين بالنسبة للوحدة المصرفة الخاصة بنا، إلا أنه من الممكن التحايل على هذا القيد باستخدام نمط النوع الجديد Newtype pattern الذي يتضمن إنشاء نوع جديد في هيكل الصف (ناقشنا هياكل الصف سابقًا في قسم "استخدام هياكل الصفوف دون حقول مسماة لإنشاء أنواع مختلفة" من الفصل <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>)، إذ سيكون لهيكل الصف حقلًا واحدًا وسيكون هناك غلافًا رفيعًا حول النوع الذي نريد تنفيذ سمة له، ثم يكون نوع الغلاف محليًا بالنسبة للوحدة المصرفة الخاصة بنا ويمكننا تنفيذ السمة على الغلاف. النوع الجديد هو مصطلح ينشأ من لغة البرمجة هاسكل Haskell. لا يوجد تأثير سلبي على وقت التنفيذ لاستخدام هذا النمط ويُستبعد نوع الغلاف في وقت التصريف.
</p>

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

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

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

</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Wrapper</span><span class="pun">(</span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;);</span><span class="pln">

impl fmt</span><span class="pun">::</span><span class="typ">Display</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">Wrapper</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn fmt</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> f</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">mut fmt</span><span class="pun">::</span><span class="typ">Formatter</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> fmt</span><span class="pun">::</span><span class="typ">Result</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        write</span><span class="pun">!(</span><span class="pln">f</span><span class="pun">,</span><span class="pln"> </span><span class="str">"[{}]"</span><span class="pun">,</span><span class="pln"> self</span><span class="pun">.</span><span class="lit">0.join</span><span class="pun">(</span><span class="str">", "</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 w </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Wrapper</span><span class="pun">(</span><span class="pln">vec</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"> </span><span class="typ">String</span><span class="pun">::</span><span class="pln">from</span><span class="pun">(</span><span class="str">"world"</span><span class="pun">)]);</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"w = {}"</span><span class="pun">,</span><span class="pln"> w</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 23: إنشاء نوع <code>Wrapper</code> حول <code>&lt;Vec&lt;String</code> لتنفيذ <code>Display</code>]
</p>

<p>
	تُستخدم <code>self.0</code> من قِبل تنفيذ <code>Display</code> للوصول إلى <code>&lt;Vec&lt;T</code> الداخلي، لأن <code>Wrapper</code> هيكل صف و <code>&lt;Vec&lt;T</code> هو العنصر في الدليل 0 في الصف. يمكننا بعد ذلك استخدام وظيفة نوع <code>Display</code> على <code>Wrapper</code>.
</p>

<p>
	الجانب السلبي لاستخدام هذه التقنية هو أن <code>Wrapper</code> هو نوع جديد لذلك لا يحتوي على توابع القيمة التي يحملها. سيتوجب علينا تنفيذ جميع توابع <code>&lt;Vec&lt;T</code> مباشرةً على <code>Wrapper</code>، بحيث تفوض هذه التوابع إلى <code>self.0</code>، ليسمح لنا بالتعامل مع <code>Wrapper</code> تمامًا مثل <code>&lt;Vec&lt;T</code>.
</p>

<p>
	إذا أردنا أن يحتوي النوع الجديد على كل تابع يمتلكه النوع الداخلي، سيكون تنفيذ سمة <code>Deref</code> (ناقشناها سابقًا في الفصل <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 مثل مراجع نمطية Regular References باستخدام سمة Deref في لغة رست</a>) على <code>Wrapper</code> لإرجاع النوع الداخلي حلًا مناسبًا. إذا كنا لا نريد أن يحتوي نوع <code>Wrapper</code> على جميع التوابع من النوع الداخلي -على سبيل المثال لتقييد سلوك نوع <code>Wrapper</code>- سيتعين علينا تنفيذ التوابع التي نريدها يدويًا فقط.
</p>

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

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch19-00-advanced-features.html" rel="external nofollow">Advanced Features</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%A3%D9%86%D9%88%D8%A7%D8%B9-%D9%88%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2148/" rel="">الأنواع والدوال المتقدمة في لغة رست</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%BA%D9%8A%D8%B1-%D8%A7%D9%84%D8%A2%D9%85%D9%86%D8%A9-unsafe-rust-r2111/" rel="">لغة رست غير الآمنة</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%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 في لغة رست</a>
	</li>
	<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</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2112</guid><pubDate>Sat, 30 Sep 2023 13:03:00 +0000</pubDate></item><item><title>&#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A; &#x63A;&#x64A;&#x631; &#x627;&#x644;&#x622;&#x645;&#x646;&#x629; Unsafe Rust</title><link>https://academy.hsoub.com/programming/rust/%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%BA%D9%8A%D8%B1-%D8%A7%D9%84%D8%A2%D9%85%D9%86%D8%A9-unsafe-rust-r2111/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_09/----Unsafe-Rust.png.7ebba13060c1447e181a2ade2d8c42fa.png" /></p>
<p>
	لدى كل الشيفرات البرمجية التي ناقشناها حتى الآن ضمانات لأمان الذاكرة في رست وتُفرض هذه الضمانات وقت التصريف، ومع ذلك فإن رست تحتوى على لغة ثانية مخبأة داخلها لا تفرض ضمانات أمان الذاكرة هذه، ويطلق عليها اسم رست غير الآمنة وتعمل تمامًا مثل رست العادية ولكنها تمنحنا قوى خارقة إضافية.
</p>

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

<p>
	السبب الآخر لوجود لغة رست غير آمنة هو أن <a href="https://academy.hsoub.com/apps/general/%D8%A7%D8%AE%D8%AA%D9%8A%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D8%AA%D8%A7%D8%AF-%D9%88%D8%A7%D9%84%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D9%81%D9%8A-%D8%A7%D9%84%D8%B9%D8%A7%D9%84%D9%85-%D8%A7%D9%84%D8%B1%D9%82%D9%85%D9%8A-r372/" rel="">عتاد الحاسب الأساسي</a> غير آمن بطبيعته، فإذا لم تسمح لك رست بإنجاز عمليات غير آمنة فلن يمكنك إنجاز مهام معينة. تحتاج رست إلى السماح لك ببرمجة الأنظمة منخفضة المستوى مثل التفاعل المباشر مع نظام التشغيل أو حتى كتابة نظام التشغيل الخاص بك. يُعد العمل مع برمجة الأنظمة منخفضة المستوى أحد أهداف اللغة. لنكتشف ما يمكننا فعله مع رست غير الآمنة وكيفية إنجاز ذلك.
</p>

<h2>
	القوى الخارقة غير الآمنة unsafe superpowers
</h2>

<p>
	استخدم الكلمة المفتاحية <code>unsafe</code> للتبديل إلى رست غير الآمنة، ثم ابدأ كتلة جديدة تحتوي على الشيفرة غير الآمنة. يمكنك اتخاذ خمسة إجراءات في رست غير الآمنة لا يمكنك فعلها في رست الآمنة ونسميها القوى الخارقة غير الآمنة؛ تتضمن هذه القوى الخارقة القدرة على:
</p>

<ul>
	<li>
		تحصيل مؤشر خام raw pointer.
	</li>
	<li>
		استدعاء تابع أو دالة غير آمنين.
	</li>
	<li>
		الوصول أو التعديل على متغير ساكن static متغيّر mutable.
	</li>
	<li>
		تطبيق سمة trait غير آمنة.
	</li>
	<li>
		حقول الوصول الخاصة بـ <code>union</code>.
	</li>
</ul>

<p>
	من المهم أن نفهم أن <code>unsafe</code> لا توقف تشغيل مدقق الاستعارة أو تعطل أي من فحوصات أمان رست الأخرى؛ وإذا كنت تستخدم مرجعًا في شيفرة غير آمنة فسيظل التحقق منه جاريًا. تمنحك الكلمة المفتاحية <code>unsafe</code> فقط الوصول إلى هذه الميزات الخمس التي لم يجري التحقق منها بعد ذلك من المصرف من أجل <a href="https://academy.hsoub.com/programming/rust/%D8%AD%D9%84%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-reference-cycles-%D9%88%D8%AA%D8%B3%D8%A8%D8%A8%D9%87%D8%A7-%D8%A8%D8%AA%D8%B3%D8%B1%D9%8A%D8%A8-%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-memory-leak-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2038/" rel="">سلامة الذاكرة</a>، وستظل تتمتع بدرجة من الأمان داخل كتلة غير آمنة. لا تعني <code>unsafe</code> أن الشيفرة الموجودة داخل الكتلة هي بالضرورة خطيرة أو أنها ستواجه بالتأكيد مشكلات تتعلق بسلامة الذاكرة، القصد هو أنه بصفتك مبرمجًا ستضمن أن الشيفرة الموجودة داخل كتلة <code>unsafe</code> ستصل إلى الذاكرة بطريقة صالحة.
</p>

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

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

<p>
	لنلقي نظرةً على كل من القوى الخارقة الخمس غير الآمنة بالترتيب، سنلقي نظرةً أيضًا على بعض الأفكار المجردة التي تقدم واجهةً آمنةً للشيفرات البرمجية غير الآمنة.
</p>

<h2>
	تحصيل مرجع مؤشر خام
</h2>

<p>
	ذكرنا سابقًا في الفصل <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> في قسم "المراجع المعلقة" أن المصرّف يضمن صلاحية المراجع دائمًا. تحتوي رست غير الآمنة على نوعين جديدين يدعيان المؤشرات الخام التي تشبه المراجع، فكما هو الحال مع المراجع يمكن أن تكون المؤشرات الخام ثابتة immutable أو متغيّرة mmutable وتُكتب بالطريقة <code>const T*</code> و <code>mut T*</code> على التوالي. لا تمثّل علامة النجمة عامل التحصيل وإنما هي جزءٌ من اسم النوع. يُقصد بمصطلح الثابت في سياق المؤشرات الخام أنه لا يمكن تعيين المؤشر مباشرةً بعد تحصيله.
</p>

<p>
	تختلف المؤشرات الأولية عن المراجع والمؤشرات الذكية بما يلي:
</p>

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

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

<p>
	توضح الشيفرة 1 كيفية إنشاء مؤشر خام ثابت ومتغيّر من المراجع.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4272_6" style=""><span class="pln">    let mut num </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span><span class="pln">

    let r1 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">num as </span><span class="pun">*</span><span class="kwd">const</span><span class="pln"> i32</span><span class="pun">;</span><span class="pln">
    let r2 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">mut num as </span><span class="pun">*</span><span class="pln">mut i32</span><span class="pun">;</span></pre>

<p style="text-align: center;">
	[الشيفرة 1: إنشاء مؤشرات خام من المراجع]
</p>

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

<p>
	أنشأنا مؤشرات خام باستعمال <code>as</code> لتحويل مرجع ثابت ومتغيّر إلى أنواع المؤشرات الخام الخاصة بهما، ونظرًا إلى أننا أنشأناها مباشرةً من مراجع مضمونة لتكون صالحة فنحن نعلم أن هذه المؤشرات الخام المعنيّة صالحة لكن لا يمكننا افتراض هذا حول أي مؤشر خام.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4272_8" style=""><span class="pln">    let address </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0x012345usize</span><span class="pun">;</span><span class="pln">
    let r </span><span class="pun">=</span><span class="pln"> address as </span><span class="pun">*</span><span class="kwd">const</span><span class="pln"> i32</span><span class="pun">;</span></pre>

<p style="text-align: center;">
	[الشيفرة 2: إنشاء مؤشر خام إلى عنوان ذاكرة عشوائي]
</p>

<p>
	تذكر أنه يمكننا إنشاء مؤشرات خام في شيفرة آمنة لكن لا يمكننا تحصيل المؤشرات الخام وقراءة البيانات التي يُشار إليها، ونستخدم في الشيفرة 3 عامل التحصيل <code>*</code> على مؤشر خام يتطلب كتلة <code>unsafe</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4272_10" style=""><span class="pln">    let mut num </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span><span class="pln">

    let r1 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">num as </span><span class="pun">*</span><span class="kwd">const</span><span class="pln"> i32</span><span class="pun">;</span><span class="pln">
    let r2 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">mut num as </span><span class="pun">*</span><span class="pln">mut i32</span><span class="pun">;</span><span class="pln">

    unsafe </span><span class="pun">{</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"r1 is: {}"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">r1</span><span class="pun">);</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"r2 is: {}"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">r2</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 3: تحصيل المؤشرات الخام ضمن كتلة <code>unsafe</code>]
</p>

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

<p>
	لاحظ أيضًا أننا أنشأنا في الشيفرة 1 و3 مؤشرات خام من النوع <code>const i32*</code> و <code>mut i32*</code> التي أشارت كلتاهما إلى موقع الذاكرة ذاته، حيث يُخزَّن <code>num</code>. إذا حاولنا إنشاء مرجع ثابت ومتغيّر إلى <code>num</code> بدلًا من ذلك، فلن تُصرّف الشيفرة لأن <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> لا تسمح بمرجع متغيّر في الوقت ذاته كما هو الحال مع أي مراجع ثابتة. يمكننا باستخدام المؤشرات الخام إنشاء مؤشر متغيّر ومؤشر ثابت للموقع ذاته وتغيير البيانات من خلال المؤشر المتغيّر مما قد يؤدي إلى إنشاء سباق بيانات data race. كن حذرًا.
</p>

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

<h2>
	استدعاء تابع أو دالة غير آمنين
</h2>

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

<p>
	فيما يلي دالة غير آمنة تدعى <code>dangerous</code> لا تنفّذ أي شيء داخلها:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4272_12" style=""><span class="pln">    unsafe fn dangerous</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

    unsafe </span><span class="pun">{</span><span class="pln">
        dangerous</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	يجب علينا استدعاء دالة <code>dangerous</code> داخل كتلة <code>unsafe</code> منفصلة، وإذا حاولنا استدعاء <code>dangerous</code> دون <code>unsafe</code> سنحصل على خطأ:
</p>

<pre class="ipsCode">$ cargo run
   Compiling unsafe-example v0.1.0 (file:///projects/unsafe-example)
error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
 --&gt; src/main.rs:4:5
  |
4 |     dangerous();
  |     ^^^^^^^^^^^ call to unsafe function
  |
  = note: consult the function's documentation for information on how to avoid undefined behavior

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

<p>
	نؤكد لرست مع الكتلة <code>unsafe</code> أننا قرأنا توثيق الدالة ونفهم كيفية استخدامها صحيحًا وتحققنا من أننا نفي بمواصفات الدالة.
</p>

<p>
	يعدّ محتوى الدالات غير الآمنة بمثابة كتل <code>unsafe</code>، لذا لا نحتاج إلى إضافة كتلة <code>unsafe</code> أخرى لأداء عمليات أخرى غير آمنة ضمن دالة غير آمنة.
</p>

<h3>
	إنشاء تجريد آمن على شيفرة غير آمنة
</h3>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4272_15" style=""><span class="pln">    let mut 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"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">];</span><span class="pln">

    let r </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">mut v</span><span class="pun">[..];</span><span class="pln">

    let </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="pun">=</span><span class="pln"> r</span><span class="pun">.</span><span class="pln">split_at_mut</span><span class="pun">(</span><span class="lit">3</span><span class="pun">);</span><span class="pln">

    assert_eq</span><span class="pun">!(</span><span class="pln">a</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="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">
    assert_eq</span><span class="pun">!(</span><span class="pln">b</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="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">]);</span></pre>

<p style="text-align: center;">
	[الشيفرة 4: استعمال الدالة الآمنة <code>split_at_mut</code>]
</p>

<p>
	لا يمكننا تنفيذ هذه الدالة باستعمال رست الآمنة فقط، وقد تبدو المحاولة السابقة مثل الشيفرة 5 التي لن تصرف. سننفّذ <code>split_at_mut</code> مثل دالة للتبسيط بدلًا من تابع لشرائح قيم <code>i32</code> فقط بدلًا من النوع العام <code>T</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4272_17" style=""><span class="pln">fn split_at_mut</span><span class="pun">(</span><span class="pln">values</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"> mid</span><span class="pun">:</span><span class="pln"> usize</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">mut </span><span class="pun">[</span><span class="pln">i32</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"> </span><span class="pun">{</span><span class="pln">
    let len </span><span class="pun">=</span><span class="pln"> values</span><span class="pun">.</span><span class="pln">len</span><span class="pun">();</span><span class="pln">

    assert</span><span class="pun">!(</span><span class="pln">mid </span><span class="pun">&lt;=</span><span class="pln"> len</span><span class="pun">);</span><span class="pln">

    </span><span class="pun">(&amp;</span><span class="pln">mut values</span><span class="pun">[..</span><span class="pln">mid</span><span class="pun">],</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">mut values</span><span class="pun">[</span><span class="pln">mid</span><span class="pun">..])</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 5: محاولة تنفيذ <code>split_at_mut</code> فقط باستعمال رست الآمنة]
</p>

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

<p>
	نعيد بعد ذلك شريحتين متغيّرتين في الصف، واحدة من بداية الشريحة الأصلية إلى الدليل <code>mid</code> والأخرى من <code>mid</code> إلى نهاية الشريحة.
</p>

<p>
	عندما نحاول تصريف الشيفرة البرمجية في الشيفرة 5 سنحصل على خطأ.
</p>

<pre class="ipsCode">$ cargo run
   Compiling unsafe-example v0.1.0 (file:///projects/unsafe-example)
error[E0499]: cannot borrow `*values` as mutable more than once at a time
 --&gt; src/main.rs:6:31
  |
1 | fn split_at_mut(values: &amp;mut [i32], mid: usize) -&gt; (&amp;mut [i32], &amp;mut [i32]) {
  |                         - let's call the lifetime of this reference `'1`
...
6 |     (&amp;mut values[..mid], &amp;mut values[mid..])
  |     --------------------------^^^^^^--------
  |     |     |                   |
  |     |     |                   second mutable borrow occurs here
  |     |     first mutable borrow occurs here
  |     returning this value requires that `*values` is borrowed for `'1`

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

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

<p>
	توضح الشيفرة 6 كيفية استخدام كتلة <code>unsafe</code> ومؤشر خام وبعض الاستدعاءات للدالات غير الآمنة لجعل تنفيذ <code>split_at_mut</code> يعمل.
</p>

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

fn split_at_mut</span><span class="pun">(</span><span class="pln">values</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"> mid</span><span class="pun">:</span><span class="pln"> usize</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">mut </span><span class="pun">[</span><span class="pln">i32</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"> </span><span class="pun">{</span><span class="pln">
    let len </span><span class="pun">=</span><span class="pln"> values</span><span class="pun">.</span><span class="pln">len</span><span class="pun">();</span><span class="pln">
    let ptr </span><span class="pun">=</span><span class="pln"> values</span><span class="pun">.</span><span class="pln">as_mut_ptr</span><span class="pun">();</span><span class="pln">

    assert</span><span class="pun">!(</span><span class="pln">mid </span><span class="pun">&lt;=</span><span class="pln"> len</span><span class="pun">);</span><span class="pln">

    unsafe </span><span class="pun">{</span><span class="pln">
        </span><span class="pun">(</span><span class="pln">
            slice</span><span class="pun">::</span><span class="pln">from_raw_parts_mut</span><span class="pun">(</span><span class="pln">ptr</span><span class="pun">,</span><span class="pln"> mid</span><span class="pun">),</span><span class="pln">
            slice</span><span class="pun">::</span><span class="pln">from_raw_parts_mut</span><span class="pun">(</span><span class="pln">ptr</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln">mid</span><span class="pun">),</span><span class="pln"> len </span><span class="pun">-</span><span class="pln"> mid</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: استعمال شيفرة غير آمنة في تنفيذ دالة <code>split_at_mut</code>]
</p>

<p>
	تذكر سابقًا من قسم "نوع الشريحة" في الفصل <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>
	نستعمل تابع <code>len</code> للحصول على طول الشريحة وتابع <code>as_mut_ptr</code> للوصول إلى المؤشر الخام للشريحة، وفي هذه الحالة نظرًا لأن لدينا شريحة متغيّرة لقيم <code>i32</code> فإن <code>as_mut_ptr</code> تُعيد مؤشرًا خامًا من النوع <code>mut i32*</code> وهو الذي خزّناه في المتغير <code>ptr</code>.
</p>

<p>
	نحافظ على التأكيد على أن الدليل <code>mid</code> يقع داخل الشريحة، ثم نبدأ بكتابة الشيفرة غير الآمنة: تأخذ الدالة <code>slice::from_raw_parts_mut</code> مؤشرًا خامًا وطولًا وتنشئ شريحة. نستخدم هذه الدالة لإنشاء شريحة تبدأ من <code>ptr</code> وتكون عناصرها بطول <code>mid</code>، ثم نستدعي التابع <code>add</code> على <code>ptr</code> مع الوسيط <code>mid</code> للحصول على مؤشر خام يبدأ من <code>mid</code> وننشئ شريحةً باستخدام هذا المؤشر والعدد المتبقي من العناصر بعد <code>mid</code> ليكون طول الشريحة.
</p>

<p>
	الدالة <code>slice::from_raw_parts_mut</code> غير آمنة لأنها تأخذ مؤشرًا خامًا ويجب أن تثق في أن هذا المؤشر صالح، كما يعد التابع <code>add</code> في المؤشرات الخام غير آمن أيضًا لأنه يجب أن تثق في أن موقع الإزاحة هو أيضًا مؤشر صالح، لذلك كان علينا وضع كتلة <code>unsafe</code> حول استدعاءات <code>slice::from_raw_parts_mut</code> و <code>add</code>حتى نتمكن من استدعائها. من خلال النظر إلى الشيفرة وإضافة التأكيد على أن <code>mid</code> يجب أن يكون أقل من أو يساوي <code>len</code> يمكننا أن نقول أن جميع المؤشرات الخام المستخدمة داخل الكتلة <code>unsafe</code> ستكون مؤشرات صالحة للبيانات داخل الشريحة، وهذا استخدام مقبول ومناسب للكتلة <code>unsafe</code>.
</p>

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

<p>
	في المقابل، من المحتمل أن يتعطل استخدام <code>slice::from_raw_parts_mut</code> في الشيفرة 7 عند استعمال الشريحة. تأخذ هذه الشيفرة موقعًا عشوائيًا للذاكرة وتنشئ شريحة يبلغ طولها 10000 عنصر.
</p>

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

    let address </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0x01234usize</span><span class="pun">;</span><span class="pln">
    let r </span><span class="pun">=</span><span class="pln"> address as </span><span class="pun">*</span><span class="pln">mut i32</span><span class="pun">;</span><span class="pln">

    let values</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">=</span><span class="pln"> unsafe </span><span class="pun">{</span><span class="pln"> slice</span><span class="pun">::</span><span class="pln">from_raw_parts_mut</span><span class="pun">(</span><span class="pln">r</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10000</span><span class="pun">)</span><span class="pln"> </span><span class="pun">};</span></pre>

<p style="text-align: center;">
	[الشيفرة 7: إنشاء شريحة من مكان ذاكرة عشوائي]
</p>

<p>
	لا نمتلك الذاكرة في هذا الموقع العشوائي وليس هناك ما يضمن أن الشريحة التي تنشئها هذه الشيفرة تحتوي على قيم <code>i32</code> صالحة، كما تؤدي محاولة استخدام <code>values</code> كما لو كانت شريحة صالحة إلى سلوك غير معرّف.
</p>

<h3>
	استعمال دوال extern لاستدعاء شيفرة خارجية
</h3>

<p>
	قد تحتاج شيفرة رست الخاصة بك أحيانًا إلى التفاعل مع شيفرة مكتوبة بلغة برمجية أخرى، لهذا تحتوي رست على الكلمة المفتاحية <code>extern</code> التي تسهل إنشاء واستخدام واجهة الدالة الخارجية Foreign Function interface‎ -أو اختصارًا FFI، وهي طريقة للغة البرمجة لتعريف الدوال وتمكين لغة برمجة (خارجية) مختلفة لاستدعاء هذه الدوال.
</p>

<p>
	توضح الشيفرة 8 التكامل مع دالة <code>abs</code> من مكتبة سي القياسية، وغالبًا ما تكون الدوال المعلنة داخل الكتل <code>extern</code> غير آمنة لاستدعائها من شيفرة رست، والسبب هو أن اللغات الأخرى لا تفرض قواعد وضمانات رست ولا يمكن لرست التحقق منها لذلك تقع مسؤولية ضمان سلامتها على عاتق المبرمج.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4272_23" style=""><span class="kwd">extern</span><span class="pln"> </span><span class="str">"C"</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn abs</span><span class="pun">(</span><span class="pln">input</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">
</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">
    unsafe </span><span class="pun">{</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"Absolute value of -3 according to C: {}"</span><span class="pun">,</span><span class="pln"> abs</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="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 8: التصريح عن الدالة <code>extern</code> واستدعاؤها في لغة أخرى]
</p>

<p>
	نُدرج ضمن كتلة <code>''extern ''C</code> أسماء وبصمات signature الدوال الخارجية من لغة أخرى نريد استدعائها، إذ يحدد الجزء <code>"C"</code> واجهة التطبيق الثنائية application binary interface‎ -أو اختصارًا ABI- التي تستخدمها الدالة الخارجية. تعرّف واجهة التطبيق الثنائية ABI كيفية استدعاء الدالة على مستوى التجميع assembly، وتعد واجهة التطبيق الثنائية للغة ''C'' الأكثر شيوعًا وتتبع واجهة التطبيق الثنائية <a href="https://academy.hsoub.com/programming/c/%D8%A8%D9%86%D9%8A%D8%A9-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-c-r1607/" rel="">للغة البرمجة سي</a>.
</p>

<h3>
	استدعاء دوال رست من لغات أخرى
</h3>

<p>
	يمكننا أيضًا استخدام <code>extern</code> لإنشاء واجهة تسمح للغات الأخرى باستدعاء دوال رست، وبدلًا من إنشاء كتلة <code>extern</code> كاملة نضيف الكلمة المفتاحية <code>extern</code> ونحدد واجهة التطبيق الثنائية ABI لاستخدامها قبل الكلمة المفتاحية <code>fn</code> للدالة ذات الصلة. نحتاج أيضًا إلى إضافة تعليق توضيحي <code>[no_mangle]#</code> لإخبار مصرّف رست بعدم تشويه mangle اسم هذه الدالة؛ إذ يحدث التشويه عندما يغير المصرف الاسم الذي أعطيناه للدالة لاسم مختلف يحتوي على مزيد من المعلومات لأجزاء أخرى من عملية التصريف لاستهلاكها ولكنها أقل قابلية للقراءة من قبل الإنسان. يشكّل كل مصرف لغة برمجية الأسماء على نحوٍ مختلف قليلًا، لذلك لكي تكون دالة رست قابلة للتسمية من اللغات الأخرى، يجب علينا تعطيل تشويه الاسم في مصرف رست.
</p>

<p>
	في المثال التالي نجعل دالة <code>call_from_c</code> قابلة للوصول من شيفرة مكتوبة بلغة سي بعد تصريفها في مكتبة مشتركة وربطها من لغة سي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4272_25" style=""><span class="com">#[no_mangle]</span><span class="pln">
pub </span><span class="kwd">extern</span><span class="pln"> </span><span class="str">"C"</span><span class="pln"> fn call_from_c</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">"Just called a Rust function from C!"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لا يتطلب استعمال <code>extern</code> الكتلة <code>unsafe</code>.
</p>

<h2>
	الوصول أو تعديل متغير ساكن قابل للتغيير mutable
</h2>

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

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4272_27" style=""><span class="kwd">static</span><span class="pln"> HELLO_WORLD</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="str">"Hello, world!"</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">"name is: {}"</span><span class="pun">,</span><span class="pln"> HELLO_WORLD</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 9: تعريف واستعمال متغير ساكن ثابت]
</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>. أسماء المتغيرات الثابتة موجودة في <code>SCREAMING_SNAKE_CASE</code> اصطلاحًا، ويمكن للمتغيرات الساكنة فقط تخزين المراجع مع دورة حياة ساكنة <code>static'</code>، ما يعني أن مصرف رست يمكنه معرفة دورة الحياة الخاصة دون الحاجة لتحديده صراحةً، ويعد الوصول إلى متغير ساكن آمنًا.
</p>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4272_29" style=""><span class="kwd">static</span><span class="pln"> mut COUNTER</span><span class="pun">:</span><span class="pln"> u32 </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

fn add_to_count</span><span class="pun">(</span><span class="pln">inc</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">
    unsafe </span><span class="pun">{</span><span class="pln">
        COUNTER </span><span class="pun">+=</span><span class="pln"> inc</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">
    add_to_count</span><span class="pun">(</span><span class="lit">3</span><span class="pun">);</span><span class="pln">

    unsafe </span><span class="pun">{</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"COUNTER: {}"</span><span class="pun">,</span><span class="pln"> COUNTER</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: القراءة من أو الكتابة على متغير ساكن قابل للتغيير غير آمن]
</p>

<p>
	كما هو الحال مع المتغيرات العادية نحدد قابلية التغيير باستخدام الكلمة المفتاحية <code>mut</code>، ويجب أن تكون أي شيفرة تقرأ أو تكتب من <code>COUNTER</code> ضمن كتلة <code>unsafe</code>. تُصرَّف هذه الشيفرة وتطبع <code>COUNTER: 3</code> كما نتوقع لأنها تستخدم خيطًا واحدًا، إذ من المحتمل أن يؤدي وصول خيوط متعددة إلى <code>COUNTER</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%AE%D9%8A%D9%88%D8%B7-threads-%D9%84%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%B1%D8%B3%D8%AA-%D8%A8%D8%B5%D9%88%D8%B1%D8%A9-%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D8%A2%D9%86%D9%8A%D9%8B%D8%A7-r2043/" rel="">استخدام الخيوط Threads لتنفيذ شيفرات رست بصورة متزامنة آنيًا</a> حيثما أمكن حتى يتحقق المصرف من أن البيانات التي يجري الوصول إليها من الخيوط المختلفة آمنة.
</p>

<h2>
	تنفيذ سمة غير آمنة
</h2>

<p>
	يمكننا استعمال <code>unsafe</code> لتطبيق سمة غير آمنة؛ وتكون السمة غير آمنة عندما يحتوي أحد توابعها على الأقل على بعض اللامتغايرات invariant التي لا يستطيع المصرف التحقق منها. نصرّح بأن السمة <code>unsafe</code> عن طريق إضافة الكلمة المفتاحية <code>unsafe</code> قبل <code>trait</code> ووضع علامة على أن تنفيذ السمة <code>unsafe</code> أيضًا كما هو موضح في الشيفرة 11.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4272_31" style=""><span class="pln">unsafe trait </span><span class="typ">Foo</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// methods go here</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

unsafe impl </span><span class="typ">Foo</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> i32 </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// method implementations go here</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 style="text-align: center;">
	[الشيفرة 11: تعريف وتنفيذ سمة غير آمنة]
</p>

<p>
	نعد بأننا سنلتزم باللا متغايرات التي لا يمكن للمصرف التحقق منها باستخدام <code>unsafe impl</code>.
</p>

<p>
	على سبيل المثال، تذكر سمات العلامة <code>Sync</code> و <code>Send</code> التي ناقشناها سابقًا في قسم "التزامن الموسع مع السمة Sync والسمة Send" في الفصل <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D8%A7%D9%84%D8%AD%D8%A7%D9%84%D8%A9-%D8%A7%D9%84%D9%85%D8%B4%D8%AA%D8%B1%D9%83%D8%A9-shared-state-concurrency-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D9%88%D8%AA%D9%88%D8%B3%D9%8A%D8%B9-%D8%A7%D9%84%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D9%85%D8%B9-send-%D9%88-sync-r2089/" rel="">تزامن الحالة المشتركة Shared-State Concurrency في لغة رست وتوسيع التزامن مع Send و Sync</a>، يطبّق المصرف هذه السمات تلقائيًا إذا كانت أنواعنا تتكون كاملًا من النوعين <code>Sync</code> و <code>Send</code>. إذا طبقنا نوعًا يحتوي على نوع ليس <code>Sync</code> و <code>Send</code> مثل المؤشرات الخام ونريد وضع علامة على هذا النوع على أنه <code>Sync</code> و <code>Send</code> فيجب علينا استخدام <code>unsafe</code>. لا تستطيع رست التحقق من أن النوع الخاص بنا يدعم الضمانات التي يمكن إرسالها بأمان عبر الخيوط أو الوصول إليها من خيوط متعددة، لذلك نحتاج إلى إجراء تلك الفحوصات يدويًا والإشارة إلى ذلك باستخدام <code>unsafe</code>.
</p>

<h2>
	الوصول لحقول الاتحاد Union
</h2>

<p>
	الإجراء الأخير الذي يعمل فقط مع <code>unsafe</code> هو الوصول إلى حقول الاتحاد؛ ويعد <code>union</code> مشابهًا للبنية <code>struct</code> ولكن يُستخدم فيه حقل مصرح واحد فقط في نسخة معينة في وقت واحد، وتُستخدم الاتحادات بصورةٍ أساسية للتفاعل مع الاتحادات في شيفرة لغة سي. يعد الوصول إلى حقول الاتحاد غير آمن لأن رست لا يمكنها ضمان نوع البيانات المخزنة حاليًا في نسخة الاتحاد. يمكنك معرفة المزيد عن الاتحادات في توثيق رست Rust Reference.
</p>

<h2>
	متى تستعمل شيفرة غير آمنة؟
</h2>

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

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch19-00-advanced-features.html" rel="external nofollow">Advanced Features</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%81%D8%A7%D9%87%D9%8A%D9%85-%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D8%B9%D9%86-%D8%A7%D9%84%D8%B3%D9%85%D8%A7%D8%AA-trait-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2112/" rel="">مفاهيم متقدمة عن السمات Trait في لغة رست</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%B5%D9%8A%D8%A7%D8%BA%D8%A9-%D8%A3%D9%86%D9%85%D8%A7%D8%B7-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D8%B5%D8%AD%D9%8A%D8%AD%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2110/" rel="">صياغة أنماط التصميم الصحيحة Pattern Syntax في لغة رست</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-%D9%85%D9%8A%D8%B2%D8%A9-%D8%AA%D9%85%D8%B1%D9%8A%D8%B1-%D8%A7%D9%84%D8%B1%D8%B3%D8%A7%D8%A6%D9%84-message-passing-%D9%84%D9%86%D9%82%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2045/" rel="">البرمجة بلغة رست استخدام ميزة تمرير الرسائل Message Passing لنقل البيانات بين الخيوط Threads في لغة رست </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="">برمجة لعبة تخمين الأرقام بلغة رست</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2111</guid><pubDate>Mon, 25 Sep 2023 13:04:00 +0000</pubDate></item><item><title>&#x635;&#x64A;&#x627;&#x63A;&#x629; &#x623;&#x646;&#x645;&#x627;&#x637; &#x627;&#x644;&#x62A;&#x635;&#x645;&#x64A;&#x645; &#x627;&#x644;&#x635;&#x62D;&#x64A;&#x62D;&#x629; Pattern Syntax &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A;</title><link>https://academy.hsoub.com/programming/rust/%D8%B5%D9%8A%D8%A7%D8%BA%D8%A9-%D8%A3%D9%86%D9%85%D8%A7%D8%B7-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D8%B5%D8%AD%D9%8A%D8%AD%D8%A9-pattern-syntax-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2110/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_09/2-------Rust.png.e403cd166b81406e8a939c7fef1aa33d.png" /></p>
<p>
	سنجمع في هذا المقال الصياغة الصالحة في الأنماط وسنتحدث عن مكان استخدام كل واحد منها.
</p>

<h2>
	مطابقة القيم المجردة Literals
</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="">التعدادات enums في لغة رست</a>. تمنحنا الشيفرة البرمجية التالية بعض الأمثلة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_6" style=""><span class="pln">    let x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">

    match x </span><span class="pun">{</span><span class="pln">
        </span><span class="lit">1</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"one"</span><span class="pun">),</span><span class="pln">
        </span><span class="lit">2</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"two"</span><span class="pun">),</span><span class="pln">
        </span><span class="lit">3</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"three"</span><span class="pun">),</span><span class="pln">
        _ </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"anything"</span><span class="pun">),</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	تطبع هذه الشيفرة <code>one</code> لأن القيمة في <code>x</code> هي 1. تُعد هذه الصياغة مفيدة عندما تريد من الشيفرة أن تنفّذ عملًا ما عندما تحصل على قيمة معينة واحدة.
</p>

<h2>
	مطابقة المتغيرات المسماة Named Variables
</h2>

<p>
	المتغيرات المُسمّاة هي أنماط غير قابلة للجدل تطابق أي قيمة، وقد استخدمناها مرات عديدة سابقًا، ولكن هناك تعقيدات عند استخدامها في تعابير <code>match</code>. ينشئ تعبير <code>match</code> نطاقًا scope جديدًا، وبالتالي ستُخفي المتغيرات المُصرّح عنها على أنها جزء من النمط داخل <code>match</code> المتغيرات التي تحمل الاسم ذاته خارج هيكل <code>match</code> كما هو الحال في جميع المتغيرات. صرّحنا ضمن الشيفرة 11 عن متغير مُسمى <code>x</code> قيمته <code>Some(5)‎</code> ومتغير <code>y</code> قيمته <code>10</code>، ثم أنشأنا تعبير <code>match</code> على القيمة <code>x</code>. ألقِ نظرةً على الأنماط في أذرع المطابقة و <code>!println</code> وحاول اكتشاف ماذا ستطبع الشيفرة قبل تنفيذ هذه الشيفرة أو متابعة القراءة.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_8" style=""><span class="pln">    let x </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 y </span><span class="pun">=</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln">

    match x </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Some</span><span class="pun">(</span><span class="lit">50</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"Got 50"</span><span class="pun">),</span><span class="pln">
        </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">y</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"Matched, y = {y}"</span><span class="pun">),</span><span class="pln">
        _ </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"Default case, x = {:?}"</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">

    println</span><span class="pun">!(</span><span class="str">"at the end: x = {:?}, y = {y}"</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">);</span></pre>

<p style="text-align: center;">
	[الشيفرة 11: تعبير <code>match</code> مع ذراع يتسبب بظهور متغير خفي <code>y</code>]
</p>

<p>
	لنرى ما سيحصل عند تنفيذ تعبير <code>match</code>؛ إذ لا تتطابق القيمة المُعرّفة <code>x</code> مع ذراع المطابقة الأول في النمط لذا يستمر تنفيذ الشيفرة.
</p>

<p>
	يتسبب النمط الموجود في ذراع المطابقة الثاني بإنشاء متغير جديد باسم <code>y</code> وهو يطابق أي قيمة داخل قيمة <code>Some</code>، وذلك لأننا في نطاق جديد داخل تعبير <code>match</code>، هذا المتغير الجديد <code>y</code>هو ليس المتغير <code>y</code> ذاته الذي صرّحنا عنه في البداية بقيمة 10. يطابق الإسناد الجديد للمتغير <code>y</code> أي قيمة داخل <code>Some</code> وهي القيمة الموجودة في <code>x</code>، وبالتالي ترتبط <code>y</code> الجديدة بقيمة <code>Some</code> الداخلية في <code>x</code>، وتبلغ تلك القيمة <code>5</code>، لذلك يُنَفَذ تعبير الذراع ويطبع <code>Matched, y = 5</code>.
</p>

<p>
	لن تُطابق الأنماط في ذراعي النمط الأولين إذا كانت القيمة في <code>x</code> هي <code>None</code> بدلًا من <code>Some(5)‎</code>، لذا ستُطابق القيم مع الشرطة السفلية underscore. لم نُضِف المتغير <code>x</code> في نمط ذراع الشرطة السفلية، لذلك يبقى <code>x</code> في التعبير هو المتغير <code>x</code> الخارجي الذي لم يُخفى، وتطبع <code>match</code> في هذه الحالة الافتراضية <code>Default case, x = None</code>.
</p>

<p>
	عندما ينتهي تعبير <code>match</code> ينتهي نطاقه أيضًا، وينتهي أيضًا نطاق المتغير <code>y</code> الداخلي. تطبع آخر تعليمة <code>println!‎</code> ما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_10" style=""><span class="pln">at the end</span><span class="pun">:</span><span class="pln"> x </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"> y </span><span class="pun">=</span><span class="pln"> </span><span class="lit">10</span></pre>

<p>
	يجب علينا استخدام درع مطابقة شرطي match guard conditional لإنشاء تعبير <code>match</code> يقارن قيم <code>x</code> و <code>y</code> الخارجية عوضًا عن تقديم متغير خفي، وسنتحدث عن ذلك لاحقّا في قسم "الشرطيات الإضافية مع دروع المطابقة".
</p>

<h2>
	الأنماط المتعددة Multiple Patterns
</h2>

<p>
	يمكننا مطابقة عدة أنماط في تعبير <code>match</code> باستخدام الصياغة <code>|</code> التي يمكن أن تكون النمط أو المعامل. نطابق في المثال التالي قيمة <code>x</code> مع أذرع المطابقة، بحيث يحتوي أول ذراع على الخَيَار "أو or"، بمعنى أنه إذا طابقت القيمة <code>x</code> أحد القيمتين في الذراع تُنفَّذ شيفرة الذراع كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_12" style=""><span class="pln">    let x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">

    match 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="lit">2</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"one or two"</span><span class="pun">),</span><span class="pln">
        </span><span class="lit">3</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"three"</span><span class="pun">),</span><span class="pln">
        _ </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"anything"</span><span class="pun">),</span><span class="pln">
    </span><span class="pun">}</span></pre>

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

<pre class="ipsCode">one or two
</pre>

<h2>
	مطابقة مجالات القيم باستخدام الصيغة =..
</h2>

<p>
	تسمح صيغة <code>=..</code> بمطابقة مجال شامل من القيم، إذ تُنفَّذ الشيفرة البرمجية الخاصة بالذراع عندما يطابق النمط أي قيمة في مجال ما كما في الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_15" style=""><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">

    match x </span><span class="pun">{</span><span class="pln">
        </span><span class="lit">1.</span><span class="pun">.=</span><span class="lit">5</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"one through five"</span><span class="pun">),</span><span class="pln">
        _ </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"something else"</span><span class="pun">),</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	تتطابق الذراع الأولى إذا كانت قيمة <code>x</code> هي 1 أو 2 أو 3 أو 4 أو 5، هذه الصياغة ملائمة لمطابقة قيم متعددة بدلًا من استخدام المعامل <code>|</code> للتعبير عن الفكرة ذاتها؛ إذا أردنا استخدام <code>|</code> فيجب تحديد <code>5 | 4 | 3 | 2 | 1</code>، وستكون طريقة تحديد المجال أقصر خاصةً إذا أردنا مطابقة أي رقم بين 1 و 1,000.
</p>

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

<p>
	إليك مثالًا يستخدم مجال من قيم <code>char</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_17" style=""><span class="pln">    let x </span><span class="pun">=</span><span class="pln"> </span><span class="str">'c'</span><span class="pun">;</span><span class="pln">

    match x </span><span class="pun">{</span><span class="pln">
        </span><span class="str">'a'</span><span class="pun">..=</span><span class="str">'j'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"early ASCII letter"</span><span class="pun">),</span><span class="pln">
        </span><span class="str">'k'</span><span class="pun">..=</span><span class="str">'z'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"late ASCII letter"</span><span class="pun">),</span><span class="pln">
        _ </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"something else"</span><span class="pun">),</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	تميز رست أن <code>'c'</code> تقع داخل مجال النمط الأول وتطبع <code>early ASCII letter</code>.
</p>

<h2>
	التفكيك Restructuring لتجزئة القيم
</h2>

<p>
	يمكننا استخدام الأنماط لتفكيك الهياكل أو المعدّدات enums أو الصفوف tuples لاستخدام الأجزاء المختلفة من القيم، لنستعرض كل حالة من الحالات السابقة.
</p>

<h3>
	تفكيك الهياكل
</h3>

<p>
	تظهر الشيفرة 12 هيكل <code>Point</code> بحقلين <code>x</code> و <code>y</code> يُمكن تجزئتهما باستخدام نمط مع التعليمة <code>let</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_19" style=""><span class="kwd">struct</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"> 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="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">0</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> </span><span class="lit">7</span><span class="pln"> </span><span class="pun">};</span><span class="pln">

    let </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"> a</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> b </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> p</span><span class="pun">;</span><span class="pln">
    assert_eq</span><span class="pun">!(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> a</span><span class="pun">);</span><span class="pln">
    assert_eq</span><span class="pun">!(</span><span class="lit">7</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;">
	[الشيفرة 12: تفكيك حقل هيكل إلى متغيرات منفصلة]
</p>

<p>
	تُنشئ الشيفرة السابقة المتغيرين <code>a</code> و <code>b</code> اللذين يطابقان قيمتي الحقلين <code>x</code> و <code>y</code> في الهيكل <code>p</code>، ويوضح هذا المثال أنه ليس من الضروري لأسماء المتغيرات في النمط أن تُطابق أسماء الحقول في الهيكل، ولكن من الشائع مطابقة أسماء المتغيرات مع أسماء الحقول لتذكر ارتباط المتغير بحقل معين. بما أن كتابة ما يلي مثلًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_21" style=""><span class="pln">let </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"> x</span><span class="pun">,</span><span class="pln"> y</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"> p</span><span class="pun">;‎</span></pre>

<p>
	تحتوي على الكثير من التكرار، لدى رست طريقةً مختصرة للأنماط التي تطابق حقول الهيكل؛ إذ عليك فقط أن تُضيف اسم حقل الهيكل مما يجعل المتغيرات المُنشأة من هذا النمط تحمل الاسم ذاته. تعمل الشيفرة 13 بطريقة عمل الشيفرة 12 ذاتها إلا أن المتغيرات المُنشأة في النمط <code>let</code> هي <code>x</code> و <code>y</code> بدلًا من <code>a</code> و <code>b</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_23" style=""><span class="kwd">struct</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"> 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="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">0</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> </span><span class="lit">7</span><span class="pln"> </span><span class="pun">};</span><span class="pln">

    let </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"> y </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> p</span><span class="pun">;</span><span class="pln">
    assert_eq</span><span class="pun">!(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">);</span><span class="pln">
    assert_eq</span><span class="pun">!(</span><span class="lit">7</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;">
	[الشيفرة 13: تفكيك حقول هيكل باستخدام طريقة حقل الهيكل المختصرة]
</p>

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

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

<p>
	لدينا في الشيفرة 14 تعبير <code>match</code> يقسم قيم <code>Point</code> إلى ثلاث حالات: نقاط تقع مباشرةً على محور <code>x</code> (الذي يُعدّ محققًا عندما <code>y = 0</code>)، ونقاط تقع على المحور <code>y</code> (أي <code>x = 0</code>)، ونقاط لا تقع على أي من المحورين.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_25" style=""><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">0</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> </span><span class="lit">7</span><span class="pln"> </span><span class="pun">};</span><span class="pln">

    match 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"> y</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"> </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"On the x axis at {x}"</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">0</span><span class="pun">,</span><span class="pln"> y </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"On the y axis at {y}"</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"> y </span><span class="pun">}</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">"On neither axis: ({x}, {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;">
	[ الشيفرة 14: تفكيك ومطابقة القيم المجردة في نمط واحد]
</p>

<p>
	ستتطابق الذراع الأولى مع أي نقطة تقع على المحور <code>x</code> عن طريق تحديد أن الحقل <code>y</code> يقابل القيمة المجردة <code>0</code>، ويُنشئ النمط متغير <code>x</code> يمكن استخدامه في شيفرة هذا الذراع؛ وبصورةٍ مشابهة، ستتطابق الذراع الثانية مع أي نقطة تقع على المحور <code>y</code> عن طريق تحديد أن الحقل <code>x</code> يقابل القيمة <code>0</code> ويُنشئ النمط متغير <code>y</code> للقيمة في الحقل <code>y</code>، ولا تحدد الذراع الثالثة أي قيمة مجرّدة لذا تُطابق أي قيمة <code>Point</code> أُخرى ويُنشأ متغيران لكل من الحقلين <code>x</code> و <code>y</code>.
</p>

<p>
	تطابِق القيمة <code>p</code> الذراع الثانية في هذا المثال بفضل احتواء <code>x</code> على 0 وتطبع هذه الشيفرة ما يلي: <code>On the y axis at 7</code>.
</p>

<p>
	تذكر أن تعبير <code>match</code> يتوقف عن التحقق من الأذرع عندما يجد أول نمط مطابق لذا حتى لو كانت <code>Point { x: 0, y: 0}‎</code> موجودةً على المحورين <code>x</code> و <code>y</code> ستطبع الشيفرة فقط <code>On the x axis at 0</code>.
</p>

<h3>
	تفكيك المعددات
</h3>

<p>
	فكّكنا سابقًا المعدّدات (الشيفرة 5 في الفصل <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>) إلا أننا لم نتحدث صراحةً أن النمط لتفكيك المعدّد يوافق طريقة تخزين البيانات داخله. تستخدم الشيفرة 15 معدّدًا يدعى <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 في لغة رست</a> وتُكتب <code>match</code> مع أنماط تفكك كل من القيم الداخلية الخاصة بذلك المعدّد.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_27" 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><span class="pln">
    let msg </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Message</span><span class="pun">::</span><span class="typ">ChangeColor</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">160</span><span class="pun">,</span><span class="pln"> </span><span class="lit">255</span><span class="pun">);</span><span class="pln">

    match msg </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Message</span><span class="pun">::</span><span class="typ">Quit</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">"The Quit variant has no data to destructure."</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="typ">Message</span><span class="pun">::</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"> y </span><span class="pun">}</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">"Move in the x direction {x} and in the y direction {y}"</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="typ">Message</span><span class="pun">::</span><span class="typ">Write</span><span class="pun">(</span><span class="pln">text</span><span class="pun">)</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">"Text message: {text}"</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="typ">Message</span><span class="pun">::</span><span class="typ">ChangeColor</span><span class="pun">(</span><span class="pln">r</span><span class="pun">,</span><span class="pln"> g</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">)</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">"Change the color to red {r}, green {g}, and blue {b}"</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>
	تطبع الشيفرة السابقة ما يلي:
</p>

<pre class="ipsCode"> Change the color to red 0, green 160, and blue 255
</pre>

<p>
	حاول تغيير قيمة <code>msg</code> لملاحظة تنفيذ الشيفرة البرمجية للأذرع الأُخرى.
</p>

<p>
	لا يمكننا تفكيك القيم أكثر من ذلك في متغيرات المعدّد التي لا تحتوي على أي بيانات مثل <code>Message::Quit</code>، إذ يمكننا فقط مطابقة القيمة المجرّدة <code>Message::Quit</code> ولا يوجد أي متغيرات في النمط.
</p>

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

<p>
	يشابه النمط الذي نستخدمه لمتغيرات المعدّدات التي تشبه الصفوف مثل <code>Message::Write</code> الذي يحتوي على صف مع عنصر واحد و <code>Message::ChangeColor</code> الذي يحتوي صف مع ثلاثة عناصر للنمط الذي نحدده لمطابقة الصفوف، ويجب أن يطابق عدد المتغيرات في النمط عدد العناصر في المتغير المُراد مطابقته.
</p>

<h3>
	تفكيك الهياكل والمعددات المتداخلة
</h3>

<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="">الهياكل structs</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="">المعدّدات enums</a> بعمق طبقة واحدة، إلا أن المطابقة تعمل على العناصر المتداخلة أيضًا، فعلى سبيل المثال يمكن إعادة بناء الشيفرة 15 لتدعم ألوان RGB و HSV في رسالة <code>ChangeColor</code> كما تبين الشيفرة 16.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_29" style=""><span class="kwd">enum</span><span class="pln"> </span><span class="typ">Color</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Rgb</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="typ">Hsv</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">

</span><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="typ">Color</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 msg </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Message</span><span class="pun">::</span><span class="typ">ChangeColor</span><span class="pun">(</span><span class="typ">Color</span><span class="pun">::</span><span class="typ">Hsv</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">160</span><span class="pun">,</span><span class="pln"> </span><span class="lit">255</span><span class="pun">));</span><span class="pln">

    match msg </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Message</span><span class="pun">::</span><span class="typ">ChangeColor</span><span class="pun">(</span><span class="typ">Color</span><span class="pun">::</span><span class="typ">Rgb</span><span class="pun">(</span><span class="pln">r</span><span class="pun">,</span><span class="pln"> g</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">))</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">"Change color to red {r}, green {g}, and blue {b}"</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="typ">Message</span><span class="pun">::</span><span class="typ">ChangeColor</span><span class="pun">(</span><span class="typ">Color</span><span class="pun">::</span><span class="typ">Hsv</span><span class="pun">(</span><span class="pln">h</span><span class="pun">,</span><span class="pln"> s</span><span class="pun">,</span><span class="pln"> v</span><span class="pun">))</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">"Change color to hue {h}, saturation {s}, value {v}"</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">(),</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 16: مطابقة معدّدات متداخلة]
</p>

<p>
	يتطابق النمط في الذراع الأولى في تعبير <code>match</code> مع متغاير معدد <code>Message::ChangeColor</code>، الذي يحتوي على متغاير معدد <code>Color::Rgb</code>، ويرتبط النمط مع قيم <code>i32</code> الداخلية الثلاث، بينما يتطابق نمط الذراع الثانية مع متغاير معدد <code>Message::ChangeColor</code> ويطابق المعدّد الداخلي <code>Color::Hsv</code>، ويمكن تحديد هذه الشروط المعقدة في تعبير <code>match</code> واحد حتى لو كان هناك معدّدان.
</p>

<h3>
	تفكيك الهياكل والصفوف
</h3>

<p>
	يمكننا مزج ومطابقة وتضمين الأنماط المفككة بطرق معقدة أكثر، ويبين المثال التالي تفكيك معقد، إذ نضمّن هياكل وصفوف داخل صف ونفكك جميع القيم الأولية primitive:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_31" style=""><span class="pln">    let </span><span class="pun">((</span><span class="pln">feet</span><span class="pun">,</span><span class="pln"> inches</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"> y </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">((</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</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">3</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> </span><span class="pun">-</span><span class="lit">10</span><span class="pln"> </span><span class="pun">});</span></pre>

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

<p>
	تُعد التجزئة مع الأنماط طريقةً مناسبة لاستخدام أجزاء من القيم مثل قيمة من كل حقل في هيكل منفصلين عن بعضهم.
</p>

<h2>
	تجاهل القيم في نمط
</h2>

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

<h3>
	تجاهل قيمة كاملة باستخدام _
</h3>

<p>
	استخدمنا الشرطة السفلية مثل نمط محرف بدل wildcard يُطابق أي قيمة ولكن لا يرتبط بها، هذا مفيد لذراع أخير في تعبير <code>match</code> ولكن يمكن استخدامه أيضًا في أي نمط مثل معاملات الدالة كما تبين الشيفرة 17.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_33" style=""><span class="pln">fn foo</span><span class="pun">(</span><span class="pln">_</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="pun">{</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"This code only uses the y parameter: {}"</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">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    foo</span><span class="pun">(</span><span class="lit">3</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></pre>

<p style="text-align: center;">
	[الشيفرة 17: استخدام <code>_</code> في بصمة الدالة]
</p>

<p>
	تتجاهل الشيفرة السابقة القيمة 3 المُمَررة مثل معامل أول تمامًا وتطبع:
</p>

<pre class="ipsCode">This code only uses the y parameter: 4
</pre>

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

<h3>
	تجاهل أجزاء من القيمة باستخدام _ متداخلة
</h3>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_35" style=""><span class="pln">    let mut setting_value </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 new_setting_value </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Some</span><span class="pun">(</span><span class="lit">10</span><span class="pun">);</span><span class="pln">

    match </span><span class="pun">(</span><span class="pln">setting_value</span><span class="pun">,</span><span class="pln"> new_setting_value</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="pun">(</span><span class="typ">Some</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">_</span><span class="pun">))</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">"Can't overwrite an existing customized value"</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">{</span><span class="pln">
            setting_value </span><span class="pun">=</span><span class="pln"> new_setting_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">

    println</span><span class="pun">!(</span><span class="str">"setting is {:?}"</span><span class="pun">,</span><span class="pln"> setting_value</span><span class="pun">);</span></pre>

<p style="text-align: center;">
	[الشيفرة 18: استخدام شرطة سفلية داخل الأنماط التي تطابق متغايرات <code>Some</code> عندما لا توجد حاجة لاستخدام القيمة داخل <code>Some</code>]
</p>

<p>
	تطبع الشيفرة ما يلي:
</p>

<pre class="ipsCode">Can't overwrite an existing customized value
</pre>

<p>
	وبعدها:
</p>

<pre class="ipsCode">setting is Some(5)‎
</pre>

<p>
	لا نحتاج لمطابقة أو استخدام القيمة في كِلا متغايري <code>Some</code> في ذراع المطابقة الأولى، لكننا بحاجة لاختبار الحالة عندما يكون متغايرا <code>Some</code> هما <code>setting_value</code> و <code>new_setting_value</code>، وفي تلك الحالة نطبع سبب عدم تغيير <code>setting_value</code> ولا تتغيّر؛ ومن أجل باقي الحالات (إذا كانت <code>setting_value</code> أو <code>new_setting_value</code> هي <code>None</code>) مُعبرة بالنمط <code>_</code> في الذراع الثانية، سنسمح للقيمة <code>new_setting_value</code> بأن تصبح <code>setting_value</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_37" style=""><span class="pln">    let numbers </span><span class="pun">=</span><span class="pln"> </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><span class="pln"> </span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">16</span><span class="pun">,</span><span class="pln"> </span><span class="lit">32</span><span class="pun">);</span><span class="pln">

    match numbers </span><span class="pun">{</span><span class="pln">
        </span><span class="pun">(</span><span class="pln">first</span><span class="pun">,</span><span class="pln"> _</span><span class="pun">,</span><span class="pln"> third</span><span class="pun">,</span><span class="pln"> _</span><span class="pun">,</span><span class="pln"> fifth</span><span class="pun">)</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">"Some numbers: {first}, {third}, {fifth}"</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;">
	[الشيفرة 19: تجاهل قيم متعددة في صف]
</p>

<p>
	تطبع الشيفرة <code>Some numbers: 2, 8, 32</code> متجاهلةً القيمتين 4 و16.
</p>

<h3>
	تجاهل المتغيرات غير المستخدمة بكتابة _ بداية اسمها
</h3>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_40" 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">
    let y </span><span class="pun">=</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 20: كتابة اسم المتغير مسبوقًا بشرطة سفلية لتجنب الحصول على تنبيه متغير غير مُستخدم]
</p>

<p>
	نحصل على تنبيه عن عدم استخدام المتغير <code>y</code> ولكن لن نحصل على تنبيه لعدم استخدام المتغير <code>‎_x</code>.
</p>

<p>
	لاحظ أن هناك اختلاف بسيط بين استخدام <code>_</code> فقط أو استخدام اسم مسبوقًا بشرطة سفلية، إذ تُسنِد الصيغة <code>‎_x</code> القيمة بالمتغير ولكن لا تُسند الصيغة <code>_</code> أي قيمة إطلاقًا، ولتوضيح أهمية الفرق إليك الشيفرة 21 التي تعطينا الخطأ التالي.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_42" style=""><span class="pln">    let s </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Some</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">

    </span><span class="kwd">if</span><span class="pln"> let </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">_s</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> s </span><span class="pun">{</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"found a string"</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">"{:?}"</span><span class="pun">,</span><span class="pln"> s</span><span class="pun">);</span></pre>

<p style="text-align: center;">
	[الشيفرة 21: يُسند متغير غير مستخدم يبدأ بشرطة سفلية إلى قيمة، مما قد يمنحه ملكية القيمة]
</p>

<p>
	سنحصل على خطأ لأن قيمة <code>s</code> ستنتقل إلى <code>s_</code> التي تمنع استخدام <code>s</code> مجددًا، بينما لا يُسند استخدام الشرطة السفلية لوحدها القيمة أبدًا. تُصرّف الشيفرة 22 التالية دون أي أخطاء لأن <code>s</code> لا تنتقل إلى <code>_</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_44" style=""><span class="pln">    let s </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Some</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">

    </span><span class="kwd">if</span><span class="pln"> let </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">_</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> s </span><span class="pun">{</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"found a string"</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">"{:?}"</span><span class="pun">,</span><span class="pln"> s</span><span class="pun">);</span></pre>

<p style="text-align: center;">
	[ الشيفرة 22: استخدام الشرطة السفلية لا يُسند القيمة]
</p>

<p>
	تعمل الشيفرة بصورةٍ صحيحة لأن <code>s</code> لا تُسند لأي قيمة ولا يتغير مكانها.
</p>

<h3>
	تجاهل الأجزاء المتبقية من القيمة باستخدام ..
</h3>

<p>
	يمكننا استخدام الصيغة <code>..</code> لاستخدام أجزاء معينة من القيمة وتجاهل الباقي وذلك مع القيم التي تحتوي على أجزاء متعددة، ودون الحاجة لاستخدام الشرطة السفلية لكل قيمة مُتَجَاهلة؛ إذ يتجاهل النمط <code>..</code> أي جزء لم يُطابق من القيمة صراحةً في باقي النمط. لدينا في الشيفرة 23 هيكل <code>Point</code> يحتوي إحداثيات في الفضاء ثلاثي الأبعاد، ونريد في تعبير <code>match</code> أن نعمل فقط على إحداثيات <code>x</code> وتتجاهل القيم الموجودة في الحقلين <code>y</code> و <code>z</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_46" style=""><span class="pln">    </span><span class="kwd">struct</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"> 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">
        z</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">

    let origin </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">0</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> z</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">

    match origin </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="pun">..</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"x is {}"</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;">
	[الشيفرة 23: تجاهل كل الحقول في <code>Point</code> عدا الحقل <code>x</code> باستخدام <code>..</code>]
</p>

<p>
	نضع القيمة <code>x</code> في قائمة وبعدها نضيف النمط <code>..</code>، وتُعد هذه الطريقة أسرع من كتابة كل من <code>y: _‎</code> و <code>z: _‎</code> وتحديدًا عند العمل مع هياكل تحتوي على العديد من الحقول وتريد الحصول على حقل واحد أو اثنين.
</p>

<p>
	يمكن زيادة الصيغة <code>..</code> إلى عدد كبير من القيم حسب الحاجة، وتظهر الشيفرة 24 كيفية استخدام <code>..</code> مع صف.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_48" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let numbers </span><span class="pun">=</span><span class="pln"> </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><span class="pln"> </span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">16</span><span class="pun">,</span><span class="pln"> </span><span class="lit">32</span><span class="pun">);</span><span class="pln">

    match numbers </span><span class="pun">{</span><span class="pln">
        </span><span class="pun">(</span><span class="pln">first</span><span class="pun">,</span><span class="pln"> </span><span class="pun">..,</span><span class="pln"> last</span><span class="pun">)</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">"Some numbers: {first}, {last}"</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;">
	[الشيفرة 24: مطابقة القيمتين الأولى والأخيرة في الصف وتجاهل باقي القيم]
</p>

<p>
	تتطابق القيمتين الأولى والأخيرة مع <code>first</code> و <code>last</code> في هذه الشيفرة، وتطابق <code>..</code> القيمتين وتتجاهل كل شيء في المنتصف.
</p>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_50" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let numbers </span><span class="pun">=</span><span class="pln"> </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><span class="pln"> </span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">16</span><span class="pun">,</span><span class="pln"> </span><span class="lit">32</span><span class="pun">);</span><span class="pln">

    match numbers </span><span class="pun">{</span><span class="pln">
        </span><span class="pun">(..,</span><span class="pln"> second</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">{</span><span class="pln">
            println</span><span class="pun">!(</span><span class="str">"Some numbers: {}"</span><span class="pun">,</span><span class="pln"> second</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;">
	[الشيفرة 25: محاولة استخدام <code>..</code> بطريقة غير واضحة]
</p>

<p>
	عندما نصرّف الشيفرة في هذا المثال نحصل على الخطأ التالي:
</p>

<pre class="ipsCode">$ cargo run
   Compiling patterns v0.1.0 (file:///projects/patterns)
error: `..` can only be used once per tuple pattern
 --&gt; src/main.rs:5:22
  |
5 |         (.., second, ..) =&gt; {
  |          --          ^^ can only be used once per tuple pattern
  |          |
  |          previously used here

error: could not compile `patterns` due to previous error
</pre>

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

<h2>
	تعابير شرطية إضافية مع دروع المطابقة
</h2>

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

<p>
	يمكن أن يستخدم الشرط متغيرات مُنشأة في النمط، وتبين الشيفرة 26 تعليمة <code>match</code> تحتوي الذراع الأولى فيها على النمط <code>Some(x)‎</code> وأيضًا درع مطابقة <code>if x % 2 == 0</code> (الذي يكون صحيحًا إذا كان العدد زوجي).
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_53" style=""><span class="pln">    let num </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Some</span><span class="pun">(</span><span class="lit">4</span><span class="pun">);</span><span class="pln">

    match num </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="kwd">if</span><span class="pln"> x </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">0</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"The number {} is even"</span><span class="pun">,</span><span class="pln"> x</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"> println</span><span class="pun">!(</span><span class="str">"The number {} is odd"</span><span class="pun">,</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"> </span><span class="pun">(),</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 26: إضافة درع مطابقة إلى نمط]
</p>

<p>
	ستطبع الشيفرة السابقة <code>The number 4 is even</code>، وتتطابق عندما تُقارن <code>num</code> مع النمط الموجود في الذراع الأولى، لأن <code>Some(4)‎</code> تطابق <code>Some(x)‎</code>. بعد ذلك، يتحقق درع المطابقة إذا كان الباقي من عملية قسمة <code>x</code> على 2 يساوي 0 ولأن هذه الحالة محقّقة يقع الاختيار على الذراع الأولى.
</p>

<p>
	سيكون درع المطابقة في الذراع الأولى خاطئًا إذا كان <code>num</code> هو <code>Some(5)‎</code>، وذلك لأن باقي قسمة 5 على 2 هو 1 وهو لا يساوي 0، وتنتقل بعدها رست إلى الذراع الثانية التي ليس فيها درع مطابقة وبالتالي تطابق أي متغير <code>Some</code>.
</p>

<p>
	لا توجد طريقة للتعبير عن شرط <code>if x % 2 == 0</code> داخل النمط، لذا يسمح لنا درع المطابقة بالتعبير عن هذا المنطق. سلبية هذا التعبير الإضافي هي أن المصرّف لا يتحقق من الشمولية عند تواجد تعابير درع مطابقة.
</p>

<p>
	ذكرنا في الشيفرة 11 أنه يمكننا استخدام دروع المطابقة لحل مشكلة إخفاء النمط pattern-shadowing. تذكر أننا أنشأنا متغيرًا جديدًا داخل النمط في التعبير <code>match</code> عوضًا عن استخدام المتغير خارج <code>match</code>، ويعني إنشاء هذا المتغير الجديد أنه لا يمكن اختبار القيم مع المتغير الخارجي. تبين الشيفرة 27 كيفية استخدام درع المطابقة لحل هذه المشكلة.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_55" 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="typ">Some</span><span class="pun">(</span><span class="lit">5</span><span class="pun">);</span><span class="pln">
    let y </span><span class="pun">=</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln">

    match x </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Some</span><span class="pun">(</span><span class="lit">50</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"Got 50"</span><span class="pun">),</span><span class="pln">
        </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> n </span><span class="pun">==</span><span class="pln"> y </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"Matched, n = {n}"</span><span class="pun">),</span><span class="pln">
        _ </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"Default case, x = {:?}"</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">

    println</span><span class="pun">!(</span><span class="str">"at the end: x = {:?}, y = {y}"</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;">
	[الشيفرة 27: استخدام درع المطابقة لاختبار المساواة مع متغير خارجي]
</p>

<p>
	ستطبع الشيفرة الآن:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_57" style=""><span class="typ">Default</span><span class="pln"> </span><span class="kwd">case</span><span class="pun">,</span><span class="pln"> x </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></pre>

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

<p>
	ليس درع المطابقة <code>if n == y</code> نمطًا وبالتالي لا يقدم أي متغيرات جديدة، إذ أن <code>y</code> هذه هي <code>y</code> الخارجية ذاتها وليست <code>y</code> مخفية جديدة، ويمكن البحث عن قيمة لديها نفس قيمة <code>y</code> الخارجية بمقارنة <code>n</code> مع <code>y</code>.
</p>

<p>
	يمكن استخدام المعامل "أو" <code>|</code> في درع المطابقة لتحديد الأنماط المتعددة، وسيُطبق شرط درع المطابقة على كل الأنماط. تبين الشيفرة 28 الأسبقية عند جمع نمط يستخدم <code>|</code> مع درع مطابقة، والقسم الأهم من هذا المثال هو درع المطابقة <code>if y</code> الذي يطبق على 4 و 5 و 6 على الرغم من أن <code>if y</code> تبدو أنها مطبقةٌ فقط على 6.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_59" style=""><span class="pln">    let x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">4</span><span class="pun">;</span><span class="pln">
    let y </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">

    match x </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="lit">5</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="lit">6</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> y </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"yes"</span><span class="pun">),</span><span class="pln">
        _ </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"no"</span><span class="pun">),</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[ الشيفرة 28: جمع عدة أنماط مع درع مطابقة]
</p>

<p>
	ينصّ شرط المطابقة على أن الذراع تتطابق فقط إذا كانت قيمة <code>x</code> تساوي 4 أو 5 أو 6 وإذا كانت قيمة <code>y</code> هي <code>true</code>، وعندما تُنفذ الشيفرة يُطَابَق نمط الذراع الأول لأن <code>x</code> هي 4 ولكن درع المطابقة <code>if y</code> خاطئ، لذا لا يقع الاختيار على الذراع الأولى وتنتقل الشيفرة إلى الذراع الثانية التي تُطابَق ويطبع البرنامج <code>no</code>، والسبب وراء ذلك هو تطبيق شرط <code>if</code> لكل النمط <code>6 | 5 | 4</code>، وليس فقط للقيمة الأخيرة 6، بمعنى أخر تتصرف أسبقية درع المطابقة مع النمط على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_61" style=""><span class="pun">(</span><span class="lit">4</span><span class="pln"> </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="lit">6</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> y </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">...</span></pre>

<p>
	عوضًا عن:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_63" style=""><span class="lit">4</span><span class="pln"> </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="pun">(</span><span class="lit">6</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> y</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">...</span></pre>

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

<h2>
	ارتباطات @
</h2>

<p>
	يسمح لنا معامل at <code>@</code> بإنشاء متغيرات تحتوي قيمة واختبارها من أجل مطابقة نمط بنفس الوقت. نريد في الشيفرة 29 اختبار حقل <code>id</code> في <code>Message::Hello</code> إذا كان ضمن المجال <code>3‎..=7</code>، ونريد أيضًا ربط القيمة إلى المتغير <code>id_variable</code> لكي نستخدمها في الشيفرة المرتبطة مع الذراع. يمكن تسمية هذا المتغير باسم الحقل <code>id</code>، لكننا استخدمنا اسمًا مختلفًا.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_65" 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">Hello</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> id</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">

    let msg </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Message</span><span class="pun">::</span><span class="typ">Hello</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> id</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">

    match msg </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Message</span><span class="pun">::</span><span class="typ">Hello</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            id</span><span class="pun">:</span><span class="pln"> id_variable </span><span class="pun">@</span><span class="pln"> </span><span class="lit">3.</span><span class="pun">.=</span><span class="lit">7</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"> println</span><span class="pun">!(</span><span class="str">"Found an id in range: {}"</span><span class="pun">,</span><span class="pln"> id_variable</span><span class="pun">),</span><span class="pln">
        </span><span class="typ">Message</span><span class="pun">::</span><span class="typ">Hello</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10.</span><span class="pun">.=</span><span class="lit">12</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">{</span><span class="pln">
            println</span><span class="pun">!(</span><span class="str">"Found an id in another range"</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="typ">Message</span><span class="pun">::</span><span class="typ">Hello</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> id </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> println</span><span class="pun">!(</span><span class="str">"Found some other id: {}"</span><span class="pun">,</span><span class="pln"> id</span><span class="pun">),</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 29: استخدام <code>@</code> لربط القيمة في النمط مع اختبارها أيضًا]
</p>

<p>
	سيطبع المثال السابق ما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4816_67" style=""><span class="typ">Found</span><span class="pln"> an id in range</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span></pre>

<p>
	نلتقط أي قيمة تطابق المجال <code>‎3..=7</code> بتحديد <code>id_variable @‎</code> قبله، إضافةً إلى اختبار نمط تلك القيمة المطابقة.
</p>

<p>
	لا تحتوي الشيفرة في الذراع متغيرًا يحتوي على قيمة حقيقية في حقل <code>id</code> في الذراع الثانية، إذ لدينا فقط مجالًا محددًا في النمط. يمكن أن تكون قيمة الحقل <code>id</code> مساوية إلى 10 أو 11 أو 12 لكن لا تعرف الشيفرة التي في ذلك النمط أي قيمة هي منهم، ولا يستطيع نمط الشيفرة استخدام القيمة من حقل <code>id</code> لأننا لم نحفظ قيمة <code>id</code> في المتغير.
</p>

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

<p>
	يسمح لنا استخدام <code>@</code> باختبار القيمة وحفظها في متغير داخل نمط واحد.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch18-00-patterns.html" rel="external nofollow">Patterns and Matching</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%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D8%BA%D9%8A%D8%B1-%D8%A7%D9%84%D8%A2%D9%85%D9%86%D8%A9-unsafe-rust-r2111/" rel="">لغة رست غير الآمنة Unsafe Rust</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D9%86%D9%85%D8%B7-%D8%AA%D8%B5%D9%85%D9%8A%D9%85%D9%8A-design-pattern-%D9%83%D8%A7%D8%A6%D9%86%D9%8A-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-object-oriented-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2108/" rel="">الأنماط Patterns واستخداماتها وقابليتها للدحض Refutability في لغة رست</a>
	</li>
	<li>
		<a href="https://wiki.hsoub.com/Design_Patterns" rel="external">توثيق أنماط التصميم</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advanced/%D8%A3%D9%86%D9%85%D8%A7%D8%B7-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A-design-patterns-r498/" rel="">أنماط التصميم البرمجي Design patterns</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/cpp/%D8%A3%D9%86%D9%85%D8%A7%D8%B7-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%88%D8%AA%D9%82%D9%86%D9%8A%D8%A7%D8%AA-%D8%A5%D8%B9%D8%A7%D8%AF%D8%A9-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%81%D9%8A-cpp-r1027/" rel="">أنماط التصميم وتقنيات إعادة التصميم في Cpp</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2110</guid><pubDate>Tue, 19 Sep 2023 13:05:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x623;&#x646;&#x645;&#x627;&#x637; Patterns &#x648;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645;&#x627;&#x62A;&#x647;&#x627; &#x648;&#x642;&#x627;&#x628;&#x644;&#x64A;&#x62A;&#x647;&#x627; &#x644;&#x644;&#x62F;&#x62D;&#x636; Refutability &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A;</title><link>https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A3%D9%86%D9%85%D8%A7%D8%B7-patterns-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D8%A7%D8%AA%D9%87%D8%A7-%D9%88%D9%82%D8%A7%D8%A8%D9%84%D9%8A%D8%AA%D9%87%D8%A7-%D9%84%D9%84%D8%AF%D8%AD%D8%B6-refutability-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2109/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_09/-Patterns----Refutability----Rust.png.249810daedf5ae9b011783a915e3cbb8.png" /></p>
<p>
	تظهر الأنماط في العديد من الأماكن في رست، وقد استخدمتها سابقًا دون ملاحظتها غالبًا. سنتحدّث في هذه المقالة عن جميع الحالات التي يكون فيها استخدام الأنماط صالحًا، إضافةً إلى الحالات التي تكون فيها قابلة للدحض Refutable.
</p>

<h2>
	أذرع تعبير match
</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="">التعدادات enums في لغة رست</a>، يمكننا استخدام الأنماط في أذرع arms تعبير <code>match</code>، ويُعرَّف التعبير <code>match</code> بالكلمة المفتاحية <code>match</code>، تليها قيمة للتطابق معها وواحد أو أكثر من أذرع المطابقة التي تتألف من نمط وتعبير يُنفذ إذا طابقت القيمة نمط الذراع على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4383_6" style=""><span class="pln">match VALUE </span><span class="pun">{</span><span class="pln">
    PATTERN </span><span class="pun">=&gt;</span><span class="pln"> EXPRESSION</span><span class="pun">,</span><span class="pln">
    PATTERN </span><span class="pun">=&gt;</span><span class="pln"> EXPRESSION</span><span class="pun">,</span><span class="pln">
    PATTERN </span><span class="pun">=&gt;</span><span class="pln"> EXPRESSION</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	على سبيل المثال إليك تعبير <code>match</code> من الشيفرة 5 (من الفصل المذكور آنفًا) الذي يطابق القيمة <code>Option&lt;i32&gt;‎</code> في المتغير <code>x</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4383_8" style=""><span class="pln">match 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"> </span><span class="typ">None</span><span class="pun">,</span><span class="pln">
    </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">i</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">i </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></pre>

<p>
	الأنماط في تعبير <code>match</code> السابق، هما: <code>None</code> و <code>Some(i)‎</code> على يسار كل سهم.
</p>

<p>
	أحد مُتطلبات تعبير <code>match</code> هو ضرورة كونه شاملًا لجميع الحالات، أي ينبغي أخذ جميع القيم المُحتملة بالحسبان في تعبير <code>match</code>. أحد الطرق لضمان تغطية شاملة الاحتمالات هي وجود نمط مطابقة مع الكل Catchall pattern للذراع الأخير، فلا يمكن مثلًا فشل تطابق اسم متغير لأي قيمة إطلاقًا وبذلك يغطي كل حالة متبقية.
</p>

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

<h2>
	تعابير if let الشرطية
</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="">التعدادات enums في لغة رست</a> عن كيفية استخدام تعابير <code>if let</code> بصورةٍ أساسية مثل طريقة مختصرة لكتابة مكافئ التعبير <code>match</code>، بحيث يطابق حالةً واحدةً فقط، ويمكن أن يكون التعبير <code>if let</code> مترافقًا مع <code>else</code> اختياريًا، بحيث يحتوي على شيفرة برمجية تُنفّذ في حال لم يتطابق النمط في <code>if let</code>.
</p>

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

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4383_10" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let favorite_color</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Option</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="typ">None</span><span class="pun">;</span><span class="pln">
    let is_tuesday </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
    let age</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Result</span><span class="pun">&lt;</span><span class="pln">u8</span><span class="pun">,</span><span class="pln"> _</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"34"</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">();</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> let </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">color</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> favorite_color </span><span class="pun">{</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"Using your favorite color, {color}, as the background"</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"> is_tuesday </span><span class="pun">{</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"Tuesday is green day!"</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"> let </span><span class="typ">Ok</span><span class="pun">(</span><span class="pln">age</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> age </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> age </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">30</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            println</span><span class="pun">!(</span><span class="str">"Using purple as the background color"</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">"Using orange as the background color"</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">
        println</span><span class="pun">!(</span><span class="str">"Using blue as the background color"</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>if let</code> و <code>else if</code> و <code>else if let</code> و <code>else</code> بنفس الوقت]
</p>

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

<p>
	يسمح لنا هذا <a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">الهيكل الشرطي</a> بدعم المتطلبات المعقدة، إذ نطبع في هذا المثال باستخدام القيم المضمّنة في الشيفرة ما يلي:
</p>

<pre class="ipsCode">Using purple as the background color
</pre>

<p>
	يمكنك ملاحظة أن التعبير <code>if let</code> تسبب بحصولنا على متغير مخفي shadowed variable بالطريقة ذاتها التي تسببت بها أذرع التعبير <code>match</code>؛ إذ يُنشئ السطر <code>if let Ok(age) = age</code> متغيرًا مخفيًا جديد يدعى <code>age</code> يحتوي على القيمة داخل المتغاير <code>Ok</code>، وهذا يعني أنه يجب إضافة الشرط <code>if age &gt; 30</code> داخل الكتلة؛ إذ لا يمكننا جمع الشَرطين في <code>if let ok(age) = age &amp;&amp; age &gt; 30</code>، والقيمة الخفية <code>age</code> التي نريد مقارنتها مع 30 غير صالحة حتى يبدأ النطاق scope الجديد بالقوس المعقوص curly bracket.
</p>

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

<h2>
	حلقات while let الشرطية
</h2>

<p>
	تسمح الحلقة الشرطية <code>while let</code> بتنفيذ حلقة <code>while</code> طالما لا يزال النمط مُطابقًا على نحوٍ مشابه لبُنية <code>if let</code>. كتبنا في الشيفرة 2 حلقة <code>while let</code> تستخدم شعاعًا مثل مكدّس stack وتطبع القيم في الشعاع بالترتيب العكسي لإدخالها.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4383_12" 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">stack</span><span class="pln"> </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="typ">stack</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
    </span><span class="typ">stack</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="lit">2</span><span class="pun">);</span><span class="pln">
    </span><span class="typ">stack</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="lit">3</span><span class="pun">);</span><span class="pln">

    </span><span class="kwd">while</span><span class="pln"> let </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">top</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">stack</span><span class="pun">.</span><span class="pln">pop</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">"{}"</span><span class="pun">,</span><span class="pln"> top</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;">
	[الشيفرة 2: استخدام حلقة <code>while let</code> لطباعة القيم طالما يُعيد استدعاء <code>stack.pop()‎</code> المتغاير <code>Some</code>]
</p>

<p>
	يطبع المثال السابق القيمة 3 ثم 2 ثم 1، إذ يأخذ التابع <code>pop</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> ويُعيد <code>Some(value)‎</code>، ويعيد القيمة <code>None</code> إذا كان الشعاع فارغًا. يستمر تنفيذ الحلقة <code>while</code> والشيفرة البرمجية داخل كتلتها طالما يُعيد استدعاء <code>pop</code> القيمة <code>Some</code>، وعندما يعيد استدعاء <code>pop</code> القيمة <code>None</code> تتوقف الحلقة، ويمكننا استخدام <code>while let</code> لإزالة pop off كل عنصر خارج المكدّس.
</p>

<h2>
	حلقات for التكرارية
</h2>

<p>
	القيمة التي تتبع الكلمة المفتاحية <code>for</code> في حلقة <code>for</code> هي النمط، فعلى سبيل المثال النمط في <code>for x in y</code> هو <code>x</code>. توضح الشيفرة 3 كيفية استخدام النمط في حلقة <code>for</code> لتفكيك destructure أو تجزئة الصف tuple إلى جزء من حلقة <code>for</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4383_14" 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="str">'a'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'b'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'c'</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">index</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">)</span><span class="pln"> in v</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">
        println</span><span class="pun">!(</span><span class="str">"{} is at index {}"</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">,</span><span class="pln"> index</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>for</code> لتفكيك صف]
</p>

<p>
	تطبع الشيفرة 3 ما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4383_16" style=""><span class="pln">$ cargo run
   </span><span class="typ">Compiling</span><span class="pln"> patterns 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/patterns)</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.52s</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">patterns</span><span class="pun">`</span><span class="pln">
a is at index </span><span class="lit">0</span><span class="pln">
b is at index </span><span class="lit">1</span><span class="pln">
c is at index </span><span class="lit">2</span></pre>

<p>
	نُعدل مكرّرًا iterator باستخدام التابع <code>enumerate</code> بحيث يعطينا قيمةً ودليلًا index لهذه القيمة ضمن صف tuple، بحيث تكون القيمة الأولى هي الصف <code>('‎0, 'a)</code>. عندما تطابق هذه القيمة النمط <code>(index, value)</code> ستكون <code>index</code> هي <code>0</code> وستكون <code>value</code> هي <code>a</code>، مما سيتسبب بطباعة السطر الأول من الخرج.
</p>

<h2>
	تعليمات let
</h2>

<p>
	تحدثنا سابقًا عن استخدام الأنماط مع <code>match</code> و <code>if let</code> فقط، إلا أننا استخدمنا الأنماط في أماكن أُخرى أيضًا مثل تعليمة <code>let</code>. ألقِ نظرةً على عملية إسناد المتغير التالية باستخدام <code>let</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4383_18" 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">
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="pun">}</span></pre>

<p>
	اِستخدمت الأنماط في كلّ مرة استخدمت فيها تعليمة <code>let</code> بالشكل السابق دون معرفتك لذلك. تكون تعليمة <code>let</code> بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4383_20" style=""><span class="pln">let PATTERN </span><span class="pun">=</span><span class="pln"> EXPRESSION</span><span class="pun">;</span></pre>

<p>
	يشكّل اسم المتغير في التعليمات المشابهة لتعليمة <code>let x = 5;‎</code> -مع اسم المتغير في فتحة <code>PATTERN</code>- نوعًا بسيطًا من الأنماط. تقارن رست التعابير مع الأنماط وتُسند أي اسم تجده، أي تتألف التعليمة <code>let x = 5‎;‎</code> من نمط هو <code>x</code> يعني "اربط ما يتطابق هنا مع المتغير <code>x</code>"، ولأن الاسم <code>x</code> يمثّل كامل النمط، فإن هذا النمط يعني "اربط كل شيء مع المتغير <code>x</code> مهما تكُن القيمة".
</p>

<p>
	لملاحظة مطابقة النمط في <code>let</code> بوضوح أكبر، جرّب الشيفرة 4 التي تستخدم نمطًا مع <code>let</code> لتفكيك الصف.
</p>

<pre class="ipsCode">    let (x, y, z) = (1, 2, 3);
</pre>

<p style="text-align: center;">
	[الشيفرة 4: استخدام نمط لتفكيك الصف وإنشاء ثلاثة متغيرات بخطوة واحدة]
</p>

<p>
	طابقنا الصف مع النمط هنا، إذ تُقارن رست القيمة <code>(3, 2, 1)</code> مع النمط <code>(x, y, z)</code> وترى أن القيمة تطابق النمط فعلًا، لذلك تربط رست <code>1</code> إلى <code>x</code>و <code>2</code> إلى <code>y</code> و <code>3</code> إلى <code>z</code>. يمكن عدّ نمط الصف هذا بمثابة تضمين ثلاثة أنماط متغيرات مفردة داخله.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4383_22" style=""><span class="pln">   let </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="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 style="text-align: center;">
	[الشيفرة 5: بناء خاطئ لنمط لا تطابق متغيراته عدد العناصر الموجودة في الصف]
</p>

<p>
	ستتسبب محاولة تصريف الشيفرة السابقة بالخطأ التالي:
</p>

<pre class="ipsCode">$ cargo run
   Compiling patterns v0.1.0 (file:///projects/patterns)
error[E0308]: mismatched types
 --&gt; src/main.rs:2:9
  |
2 |     let (x, y) = (1, 2, 3);
  |         ^^^^^^   --------- this expression has type `({integer}, {integer}, {integer})`
  |         |
  |         expected a tuple with 3 elements, found one with 2 elements
  |
  = note: expected tuple `({integer}, {integer}, {integer})`
             found tuple `(_, _)`

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

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

<h2>
	معاملات الدالة Function Parameters
</h2>

<p>
	يمكن لمعاملات الدالة أن تمثّل أنماطًا. ينبغي أن تكون الشيفرة 6 مألوفةً بالنسبة لك، إذ نصرّح فيها عن دالة اسمها <code>foo</code> تقبل معاملًا واحدًا اسمه <code>x</code> من النوع <code>i32</code>.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4383_25" style=""><span class="pln">fn foo</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">{</span><span class="pln">
    </span><span class="com">// تُكتب الشيفرة البرمجية هنا</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 6: بصمة دالة function signature تستخدم الأنماط في معاملاتها]
</p>

<p>
	يشكّل الجزء <code>x</code> نمطًا. يمكننا مطابقة الصف في وسيط الدالة مع النمط كما فعلنا مع <code>let</code>. تجزِّء الشيفرة 7 القيم الموجودة في الصف عندما نمررها إلى الدالة.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4383_27" style=""><span class="pln">fn print_coordinates</span><span class="pun">(&amp;(</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">&amp;(</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">
    println</span><span class="pun">!(</span><span class="str">"Current location: ({}, {})"</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">

fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let point </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">);</span><span class="pln">
    print_coordinates</span><span class="pun">(&amp;</span><span class="pln">point</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 7: دالة تفكك الصف مع معاملاتها]
</p>

<p>
	تطبع الشيفرة السابقة: <code>Current location: (3, 5)‎</code>، إذ تطابق القيم <code>‎&amp;(3, 5)‎</code> النمط <code>‎&amp;(x, y)‎</code>، لذا فإن قيمة <code>x</code> هي <code>3</code> وقيمة <code>y</code> هي <code>5</code>.
</p>

<p>
	يمكننا أيضًا استخدام الأنماط في قوائم معاملات التغليف closure parameter lists بطريقة قوائم معاملات الدالة function parameter lists ذاتها، لأن المغلفات مشابهة للدالات كما رأينا سابقًا في الفصل <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 في لغة رست</a>.
</p>

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

<h2>
	قابلية الدحض refutability واحتمالية فشل مطابقة النمط
</h2>

<p>
	تأتي الأنماط بشكلين: قابلة للدحض أو النقض refutable وغير قابلة للدحض irrefutable، إذ تُدعى الأنماط التي تطابق أي قيمة تمرر خلالها بالأنماط القابلة للدحض، ومثال على ذلك هو <code>x</code> في التعليمة <code>let x = 5;‎</code> وذلك لأن المتغير <code>x</code> سيطابق أي شيء وبالتالي لا تفشل المطابقة؛ بينما تُدعى الأنماط التي تفشل في بعض القيم بالأنماط القابلة للدحض، ومثال على ذلك هو <code>Some(x)‎</code> في التعبير <code>if let Some(x) = a_value</code> لأن النمط <code>Some(x)‎</code> لن يُطابق إذا كانت القيمة في المتغير <code>a_value</code> هي <code>None</code> عوضًا عن <code>Some</code>.
</p>

<p>
	تقبل معاملات الدالة وتعليمات <code>let</code> وحلقات <code>for</code> فقط الأنماط غير القابلة للدحض لأن البرنامج لا يستطيع عمل أي شيء مفيد عندما لا تتطابق القيم. يقبل التعبيران <code>if let</code> و <code>while let</code> الأنماط القابلة للدحض وغير القابلة للدحض، إلا أنّ المصرّف يحذّر من استخدام الأنماط غير القابلة للدحض، لأنها -بحسب تعريفها- ليست معدّة لتتعامل مع فشل محتمل، إذ تتمثّل الوظيفة الشرطية بقدرتها على التصرف بصورةٍ مختلفة اعتمادًا على النجاح أو الفشل.
</p>

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

<p>
	لنتابع مثالّا لما قد يحصل عندما نجرب استخدام نمط قابل للدحض عندما تتطلب رست نمطّا غير قابل للدحض -والعكس صحيح- إذ تبيّن الشيفرة 8 تعليمة <code>let</code> إلا أن النمط الذي حددناه هو <code>Some(x)‎</code> وهو نمط قابل للدحض، ولن تُصرَّف الشيفرة كما هو متوقع.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4383_29" style=""><span class="pln">let </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">=</span><span class="pln"> some_option_value</span><span class="pun">;</span></pre>

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

<p>
	ستفشل مطابقة النمط في <code>Some(x)‎</code> إذا كانت القيمة في <code>some_option_value</code> هي <code>None</code> أي أن النمط هو قابل للدحض، ولكن تقبل تعليمة <code>let</code> فقط الأنماط غير القابلة للدحض لأنه لا توجد قيمة صالحة تستطيع الشيفرة استخدامها مع قيمة <code>None</code>. تنبّهنا رست عند استخدام قيمة قابلة للدحض عندما يتطلب الأمر وجود قيمة غير قابلة للدحض وقت التصريف:
</p>

<pre class="ipsCode">$ cargo run
   Compiling patterns v0.1.0 (file:///projects/patterns)
error[E0005]: refutable pattern in local binding: `None` not covered
 --&gt; src/main.rs:3:9
  |
3 |     let Some(x) = some_option_value;
  |         ^^^^^^^ pattern `None` not covered
  |
  = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
  = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
note: `Option&lt;i32&gt;` defined here
 --&gt; /rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/option.rs:518:1
  |
  = note: 
/rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/option.rs:522:5: not covered
  = note: the matched value is of type `Option&lt;i32&gt;`
help: you might want to use `if let` to ignore the variant that isn't matched
  |
3 |     let x = if let Some(x) = some_option_value { x } else { todo!() };
  |     ++++++++++                                 ++++++++++++++++++++++
help: alternatively, you might want to use let else to handle the variant that isn't matched
  |
3 |     let Some(x) = some_option_value else { todo!() };
  |                                     ++++++++++++++++

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

<p>
	تُعطينا رست الخطأ التصريفي السابق، وذلك بسبب عدم تغطيتنا لكل القيم الممكنة مع النمط <code>Some(x)‎</code>، ولن نستطيع فعل ذلك حتى لو أردنا.
</p>

<p>
	يمكننا إصلاح المشكلة في حال وجود نمط قابل للدحض يحل مكان نمط غير قابل للدحض عن طريق تغيير الشيفرة التي تستخدم النمط؛ فبدلًا من استخدام <code>let</code> نستخدم <code>if let</code>، وهكذا إذا لم يُطابق النمط تتخطى الشيفرة تلك الشيفرة الموجودة في القوسين المعقوصين وتستمر بذلك صلاحية الشيفرة. تبيّن الشيفرة 9 كيفية إصلاح الخطأ في الشيفرة 8.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4383_31" style=""><span class="pln">    </span><span class="kwd">if</span><span class="pln"> let </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">=</span><span class="pln"> some_option_value </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"> x</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 9: استخدام <code>if let</code> وكتلة تحتوي على أنماط قابلة للدحض بدلًا من <code>let</code>]
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4383_33" 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="kwd">if</span><span class="pln"> let x </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">
        println</span><span class="pun">!(</span><span class="str">"{}"</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="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 10: محاولة استخدام نمط غير قابل للدحض مع <code>if let</code>]
</p>

<p>
	تشتكي رست من عدم منطقيّة استخدام <code>if let</code> مع نمط غير قابل للدحض:
</p>

<pre class="ipsCode">$ cargo run
   Compiling patterns v0.1.0 (file:///projects/patterns)
warning: irrefutable `if let` pattern
 --&gt; src/main.rs:2:8
  |
2 |     if let x = 5 {
  |        ^^^^^^^^^
  |
  = note: this pattern will always match, so the `if let` is useless
  = help: consider replacing the `if let` with a `let`
  = note: `#[warn(irrefutable_let_patterns)]` on by default

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

<p>
	يجب أن تستخدم مطابقة الأذرع match arms الأنماط القابلة للدحض لهذا السبب ما عدا الذراع الأخير، الذي يجب أن يطابق أي قيمة متبقية من النمط غير القابل للدحض. تسمح رست باستخدام نمط غير قابل للدحض في <code>match</code> باستخدام ذراع واحد فقط، ولكن الصياغة هذه ليست مفيدة ويمكن استبدالها بتعليمة <code>let</code> أبسط.
</p>

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

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch18-00-patterns.html" rel="external nofollow">Patterns and Matching</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%B5%D9%8A%D8%A7%D8%BA%D8%A9-%D8%A3%D9%86%D9%85%D8%A7%D8%B7-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D8%B5%D8%AD%D9%8A%D8%AD%D8%A9-pattern-syntax-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2110/" rel="">صياغة أنماط التصميم الصحيحة Pattern Syntax في لغة رست</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D9%86%D9%85%D8%B7-%D8%AA%D8%B5%D9%85%D9%8A%D9%85%D9%8A-design-pattern-%D9%83%D8%A7%D8%A6%D9%86%D9%8A-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-object-oriented-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2108/" rel="">تنفيذ نمط تصميمي Design Pattern كائني التوجه Object-Oriented في لغة رست</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advanced/%D8%A3%D9%86%D9%85%D8%A7%D8%B7-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A-design-patterns-r498/" rel="">أنماط التصميم البرمجي Design patterns</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/cpp/%D8%A3%D9%86%D9%85%D8%A7%D8%B7-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%88%D8%AA%D9%82%D9%86%D9%8A%D8%A7%D8%AA-%D8%A5%D8%B9%D8%A7%D8%AF%D8%A9-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%81%D9%8A-cpp-r1027/" rel="">أنماط التصميم وتقنيات إعادة التصميم في Cpp</a>
	</li>
	<li>
		<a href="https://wiki.hsoub.com/Design_Patterns" rel="external">توثيق أنماط التصميم</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2109</guid><pubDate>Sat, 09 Sep 2023 13:00:00 +0000</pubDate></item><item><title>&#x62A;&#x646;&#x641;&#x64A;&#x630; &#x646;&#x645;&#x637; &#x62A;&#x635;&#x645;&#x64A;&#x645;&#x64A; Design Pattern &#x643;&#x627;&#x626;&#x646;&#x64A; &#x627;&#x644;&#x62A;&#x648;&#x62C;&#x647; Object-Oriented &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A;</title><link>https://academy.hsoub.com/programming/rust/%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D9%86%D9%85%D8%B7-%D8%AA%D8%B5%D9%85%D9%8A%D9%85%D9%8A-design-pattern-%D9%83%D8%A7%D8%A6%D9%86%D9%8A-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-object-oriented-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2108/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_09/---Design-Pattern---Object-Oriented----Rust.png.1a6377a7aa6c2512a050f1eab08f17bd.png" /></p>
<p>
	<a href="https://wiki.hsoub.com/Design_Patterns/state" rel="external">نمط الحالة state pattern</a> هو نمط تصميم كائني التوجه Object-Oriented، والمغزى منه هو أننا نعرّف مجموعةً من الحالات التي يمكن للقيمة أن تمتلكها داخليًا، وتُمثَّل الحالات من خلال مجموعة من كائنات الحالة state objects، ويتغير سلوك القيمة بناءً على حالتها. سنعمل من خلال مثال لهيكل منشور مدونة يحتوي على حقل للاحتفاظ بحالته، والتي ستكون كائن حالة من مجموعة القيم "مسودة draft" أو " قيد المراجعة review" أو "منشور published".
</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="">الهياكل structs</a> و<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> بدلًا من الكائنات objects والوراثة inheritance في لغة رست. كل كائن حالة مسؤول عن سلوكه الخاص وعن تحديد متى يجب عليه أن يتغير من حالة إلى أخرى. لا تعرف القيمة التي تخزّن كائن الحالة شيئًا عن السلوك المختلف للحالات أو متى تنتقل بينها.
</p>

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

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

<p>
	ستكون النتيجة النهائية كما يلي:
</p>

<ol>
	<li>
		يبدأ منشور المدونة مثل مسودة فارغة.
	</li>
	<li>
		يُطلب مراجعة المنشور عند الانتهاء من المسودة.
	</li>
	<li>
		يُنشر المنشور عندما يُوافَق عليه.
	</li>
	<li>
		منشورات المدونة التي هي بحالة "منشور published" هي المنشورات الوحيدة التي تعيد محتوًى ليُطبع، بحيث لا يمكن نشر المنشورات غير الموافق عليها عن طريق الخطأ.
	</li>
</ol>

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

<p>
	تظهر الشيفرة 11 سير العمل هذا على هيئة شيفرة برمجية، وهذا مثال عن استعمال الواجهة البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> التي سننفّذها في وحدة مكتبة مصرّفة library crate تسمى <code>blog</code>. لن تُصرَّف الشيفرة البرمجية التالية بنجاح، لأننا لم ننفّذ الوحدة المصرفة <code>blog</code> بعد.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5411_6" style=""><span class="pln">use blog</span><span class="pun">::</span><span class="typ">Post</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 post </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Post</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">();</span><span class="pln">

    post</span><span class="pun">.</span><span class="pln">add_text</span><span class="pun">(</span><span class="str">"I ate a salad for lunch today"</span><span class="pun">);</span><span class="pln">
    assert_eq</span><span class="pun">!(</span><span class="str">""</span><span class="pun">,</span><span class="pln"> post</span><span class="pun">.</span><span class="pln">content</span><span class="pun">());</span><span class="pln">

    post</span><span class="pun">.</span><span class="pln">request_review</span><span class="pun">();</span><span class="pln">
    assert_eq</span><span class="pun">!(</span><span class="str">""</span><span class="pun">,</span><span class="pln"> post</span><span class="pun">.</span><span class="pln">content</span><span class="pun">());</span><span class="pln">

    post</span><span class="pun">.</span><span class="pln">approve</span><span class="pun">();</span><span class="pln">
    assert_eq</span><span class="pun">!(</span><span class="str">"I ate a salad for lunch today"</span><span class="pun">,</span><span class="pln"> post</span><span class="pun">.</span><span class="pln">content</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 11: الشيفرة التي تُظهر السلوك المرغوب الذي نريد لوحدتنا المصرفة <code>blog</code> أن تحتويه]
</p>

<p>
	نريد السماح للمستخدم بإنشاء مسودة منشور مدونة جديد باستعمال <code>Post::new</code>، كما نريد السماح بإضافة نص إلى منشور المدونة، إذ يجب ألّا نحصل على أي نص إذا حاولنا الحصول على محتوى المنشور فورًا قبل الموافقة وذلك لأن المنشور لا يزال في وضع المسودة. أضفنا <code>assert_eq!‎</code> في الشيفرة البرمجية لأغراض توضيحية. قد يكون اختبار الوحدة unit test المناسب لهذا هو التأكيد على أن مسودة منشور مدونة تعرض سلسلةً نصيةً string فارغة من تابع <code>content</code> لكننا لن نكتب أي اختبارات لهذا المثال حاليًا.
</p>

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

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

<h2>
	تعريف المنشور وإنشاء نسخة جديدة في حالة المسودة
</h2>

<p>
	لنبدأ بتنفيذ المكتبة؛ نعلم أننا بحاجة إلى هيكل عام public struct يدعى <code>Post</code> ليخزّن محتوى المنشور، لذا سنبدأ بتعريف الهيكل ودالة عامة مرتبطة associated تدعى <code>new</code> لإنشاء نسخة من <code>Post</code> كما هو موضح في الشيفرة 12. سننشئ أيضًا سمةً خاصة تدعى <code>State</code> تعرّف السلوك الذي يجب أن تتمتع به جميع كائنات حالة <code>Post</code>.
</p>

<p>
	سيخزّن هيكل <code>Post</code> بعد ذلك كائن السمة <code>&lt;Box&lt;dyn State</code> داخل <code>&lt;Option&lt;T</code> في حقل خاص يسمى <code>state</code> ليخزّن كائن الحالة، وسترى سبب ضرورة <code>&lt;Option&lt;T</code> بعد قليل.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5411_8" style=""><span class="pln">pub </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Post</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    state</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Option</span><span class="pun">&lt;</span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">State</span><span class="pun">&gt;&gt;,</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">
</span><span class="pun">}</span><span class="pln">

impl </span><span class="typ">Post</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"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Post</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Post</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            state</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Some</span><span class="pun">(</span><span class="typ">Box</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Draft</span><span class="pln"> </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="kwd">new</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">

trait </span><span class="typ">State</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">Draft</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

impl </span><span class="typ">State</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">Draft</span><span class="pln"> </span><span class="pun">{}</span></pre>

<p style="text-align: center;">
	[الشيفرة 12: تعريف هيكل <code>Post</code> ودالة <code>new</code> التي تنشئ نسخة من <code>Post</code> وسمة <code>State</code> وهيكل <code>Draft</code> جدد]
</p>

<p>
	تعرّف السمة <code>State</code> السلوك المشترك بين حالات النشر المختلفة. كائنات الحالة هي <code>Draft</code> و <code>PendingReview</code> و <code>Published</code> وسوف تنفِّذ جميعها سمة <code>State</code>. لا تحتوي السمة في الوقت الحالي على أي توابع وسنبدأ بتعريف حالة <code>Draft</code> فقط لأن هذه هي الحالة التي نريد أن يبدأ المنشور فيها.
</p>

<p>
	عندما ننشئ <code>Post</code> جديد نعيّن حقل <code>state</code> الخاص به بقيمة <code>Some</code> التي تحتوي على <code>Box</code>، وتشير <code>Box</code> هنا إلى نسخة جديدة للهيكل <code>Draft</code>، أي أنه عندما ننشئ نسخةً جديدة من <code>Post</code> فإنها ستبدأ مثل مسودة. نظرًا لأن حقل <code>state</code> للهيكل <code>Post</code>هو خاص ولا توجد طريقةٌ لإنشاء <code>Post</code> في أي حالة أخرى. عيّننا سلسلة نصية <code>String</code> فارغة جديدة للحقل <code>content</code> في الدالة <code>Post::new</code>.
</p>

<h2>
	تخزين نص محتوى المنشور
</h2>

<p>
	كنا في الشيفرة 11 قادرين على استدعاء تابع يسمى <code>add_text</code> وتمرير<code>str&amp;</code> إليه ليُضاف لاحقًا على أنه محتوى نص منشور المدونة. ننفّذ هذا مثل تابع بدلًا من عرض حقل <code>content</code> على أنه <code>pub</code> حتى نتمكن لاحقًا من تنفيذ تابع يتحكم بكيفية قراءة بيانات حقل <code>content</code>. تابع <code>add_text</code> واضح جدًا، لذا سنضيف التنفيذ في الشيفرة 13 إلى كتلة <code>impl Post</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5411_10" style=""><span class="pln">impl </span><span class="typ">Post</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
    pub fn add_text</span><span class="pun">(&amp;</span><span class="pln">mut self</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">{</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">content</span><span class="pun">.</span><span class="pln">push_str</span><span class="pun">(</span><span class="pln">text</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>add_text</code> لإضافة نص لمنشور <code>content</code>]
</p>

<p>
	يأخذ تابع <code>add_text</code> مرجعًا متغيّرًا mutable إلى <code>self</code> لأننا نعدّل نسخة <code>Post</code> التي نستدعي <code>add_text</code> عليها، ثم نستدعي <code>push_str</code> على <code>String</code> في <code>content</code> ونمرّر الوسيط <code>text</code> لإضافتها إلى <code>content</code> المحفوظ. لا يعتمد هذا السلوك على حالة المنشور لذا فهو ليس جزءًا من نمط الحالة. لا يتفاعل تابع <code>add_text</code> مع حقل <code>state</code> إطلاقًا، لكنه جزءٌ من السلوك الذي نريد دعمه.
</p>

<h2>
	ضمان أن محتوى مسودة المنشور فارغ
</h2>

<p>
	ما زلنا بحاجة تابع <code>content</code> حتى بعد استدعائنا <code>add_text</code> وإضافة بعض المحتوى إلى منشورنا وذلك لإعادة شريحة سلسلة نصية string slice فارغة لأن المنشور لا يزال في حالة المسودة كما هو موضح في السطر 7 من الشيفرة 11. لننفّذ حاليًا تابع <code>content</code> بأبسط شيء يستوفي هذا المتطلب؛ وهو إعادة شريحة سلسلة نصية فارغة دائمًا، إلا أننا سنغير هذا لاحقًا بمجرد تقديم القدرة على تغيير حالة المنشور حتى يمكن نشره. حتى الآن يمكن أن تكون المنشورات في حالة المسودة فقط لذلك يجب أن يكون محتوى المنشور فارغًا دائمًا. تُظهر الشيفرة 14 تنفيذ الموضع المؤقت هذا.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5411_12" style=""><span class="pln">impl </span><span class="typ">Post</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
    pub fn content</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">str </span><span class="pun">{</span><span class="pln">
        </span><span class="str">""</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>content</code> على <code>Post</code> يُعيد دائمًا شريحة سلسلة فارغة]
</p>

<p>
	يعمل الآن كل شيء كما خُطّط له مع إضافة تابع <code>content</code> في الشيفرة 11 حتى السطر 7.
</p>

<h2>
	طلب مراجعة للمنشور يغير حالته
</h2>

<p>
	نحتاج بعد ذلك إلى إضافة إمكانية طلب مراجعة منشور وتغيير حالته من <code>Draft</code> إلى <code>PendingReview</code>، وتوضّح الشيفرة 15 هذا الأمر.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5411_14" style=""><span class="pln">impl </span><span class="typ">Post</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
    pub fn request_review</span><span class="pun">(&amp;</span><span class="pln">mut 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"> let </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">s</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">state</span><span class="pun">.</span><span class="pln">take</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">state </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">s</span><span class="pun">.</span><span class="pln">request_review</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">

trait </span><span class="typ">State</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn request_review</span><span class="pun">(</span><span class="pln">self</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="typ">Self</span><span class="pun">&gt;)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">State</span><span class="pun">&gt;;</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">Draft</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

impl </span><span class="typ">State</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">Draft</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn request_review</span><span class="pun">(</span><span class="pln">self</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="typ">Self</span><span class="pun">&gt;)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">State</span><span class="pun">&gt;</span><span class="pln"> </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">PendingReview</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">struct</span><span class="pln"> </span><span class="typ">PendingReview</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

impl </span><span class="typ">State</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">PendingReview</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn request_review</span><span class="pun">(</span><span class="pln">self</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="typ">Self</span><span class="pun">&gt;)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">State</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        self
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 15: تنفيذ توابع <code>request_review</code> على <code>Post</code> وسمة <code>State</code>]
</p>

<p>
	نعطي <code>Post</code> تابعًا عام يدعى <code>request_review</code>، والذي سيأخذ مرجعًا متغيّرًا يشير إلى <code>self</code>، نستدعي بعد ذلك التابع <code>request_review</code> الداخلي على الحالة <code>Post</code> الحالية، إذ يستخدم <code>request_review</code> الثاني الحالة الحالية ويعيد حالةً جديدة.
</p>

<p>
	نضيف التابع <code>request_review</code> إلى السمة <code>State</code>؛ إذ ستحتاج جميع الأنواع التي تطبق السمة الآن إلى تنفيذ تابع <code>request_review</code>. لاحظ أن لدينا <code>&lt;self: Box&lt;Self</code> بدلًا من <code>self</code>، أو <code>self&amp;</code>، أو <code>mut self&amp;</code> بمثابة معامل أول للتابع. تعني هذه الصيغة أن التابع صالحٌ فقط عندما يُستدعى على <code>Box</code> يحتوي النوع. تأخذ هذه الطريقة بالصياغة ملكية <code>&lt;Box&lt;Self</code> مما يبطل الحالة القديمة بحيث يمكن أن تتحول قيمة حالة <code>Post</code> إلى حالة جديدة.
</p>

<p>
	يجب أن يأخذ تابع <code>request_review</code> ملكية قيمة الحالة القديمة لاستخدامها، وهذه هي فائدة استخدام <code>Option</code> في حقل <code>state</code> الخاص بالمنشور <code>Post</code>؛ إذ نستدعي تابع <code>take</code> لأخذ قيمة <code>Some</code> من حقل <code>state</code> وترك <code>None</code> في مكانها لأن رست لا تسمح لنا بامتلاك حقول غير مأهولة في الهياكل. يتيح لنا ذلك نقل قيمة <code>state</code> خارج <code>Post</code> بدلًا من استعارتها. ثم نعيّن قيمة <code>state</code> للمنشور بنتيجة هذه العملية.
</p>

<p>
	نحتاج إلى جعل <code>state</code> مساوية للقيمة <code>None</code> مؤقتًا بدلًا من تعيينها مباشرةً باستعمال شيفرة برمجية مثل:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5411_16" style=""><span class="pln"> self</span><span class="pun">.</span><span class="pln">state </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">state</span><span class="pun">.</span><span class="pln">request_review</span><span class="pun">();</span></pre>

<p>
	للحصول على ملكية قيمة <code>state</code>، ويضمن لنا ذلك أن <code>Post</code> لا يمكنه استخدام قيمة <code>state</code> القديمة بعد أن حوّلناها إلى حالة جديدة.
</p>

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

<p>
	يمكننا الآن البدء في رؤية مزايا نمط الحالة، فتابع <code>request_review</code> على <code>Post</code> هو نفسه بغض النظر عن قيمة <code>state</code>، فكل حالة مسؤولة عن قواعدها الخاصة.
</p>

<p>
	سنترك تابع <code>content</code> على <code>Post</code> كما هو ونعيد شريحة سلسلة نصية string slice فارغة. يمكننا الآن الحصول على <code>Post</code> في حالة <code>PendingReview</code> وكذلك في حالة <code>Draft</code> إلا أننا نريد السلوك ذاته في حالة <code>PendingReview</code>. تعمل الشيفرة 11 الآن بنجاح وصولًا للسطر 10.
</p>

<h2>
	إضافة approve لتغيير سلوك التابع content
</h2>

<p>
	سيكون تابع <code>approve</code> مشابهًا لتابع <code>request_review</code>، إذ سيضبط قيمة <code>state</code> على القيمة التي تقول الحالة الحالية أنها يجب أن تكون عليها عند الموافقة على تلك الحالة كما هو موضح في الشيفرة 16.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5411_18" style=""><span class="pln">impl </span><span class="typ">Post</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
    pub fn approve</span><span class="pun">(&amp;</span><span class="pln">mut 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"> let </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">s</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">state</span><span class="pun">.</span><span class="pln">take</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">state </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">s</span><span class="pun">.</span><span class="pln">approve</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">

trait </span><span class="typ">State</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn request_review</span><span class="pun">(</span><span class="pln">self</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="typ">Self</span><span class="pun">&gt;)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">State</span><span class="pun">&gt;;</span><span class="pln">
    fn approve</span><span class="pun">(</span><span class="pln">self</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="typ">Self</span><span class="pun">&gt;)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">State</span><span class="pun">&gt;;</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">Draft</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

impl </span><span class="typ">State</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">Draft</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
    fn approve</span><span class="pun">(</span><span class="pln">self</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="typ">Self</span><span class="pun">&gt;)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">State</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</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">struct</span><span class="pln"> </span><span class="typ">PendingReview</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

impl </span><span class="typ">State</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">PendingReview</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
    fn approve</span><span class="pun">(</span><span class="pln">self</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="typ">Self</span><span class="pun">&gt;)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">State</span><span class="pun">&gt;</span><span class="pln"> </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">Published</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">struct</span><span class="pln"> </span><span class="typ">Published</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

impl </span><span class="typ">State</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">Published</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn request_review</span><span class="pun">(</span><span class="pln">self</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="typ">Self</span><span class="pun">&gt;)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">State</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        self
    </span><span class="pun">}</span><span class="pln">

    fn approve</span><span class="pun">(</span><span class="pln">self</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="typ">Self</span><span class="pun">&gt;)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">State</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        self
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 16: تنفيذ تابع <code>approve</code> على <code>Post</code> وسمة <code>State</code>]
</p>

<p>
	نضيف تابع <code>approve</code> إلى سمة <code>State</code> ونضيف هيكلًا جديدًا ينفّذ السمة <code>State</code>، والحالة <code>Published</code>.
</p>

<p>
	لن يكون للتابع <code>approve</code> على <code>Draft</code> عند استدعائه أي تأثير على غرار الطريقة التي يعمل بها <code>request_review</code> في <code>PendingReview</code>، وذلك لأن التابع <code>approve</code> سيعيد <code>self</code>، بينما يعيد التابع <code>approve</code> عندما نستدعيه على <code>PendingReview</code> نسخةً جديدةً ضمن صندوق boxed لهيكل <code>Published</code>. ينفّذ هيكل <code>Published</code>السمة <code>State</code> وتعيد نفسها من أجل كل من تابع <code>request_review</code> وتابع <code>approve</code> لأن المنشور يجب أن يبقى في حالة <code>Published</code> في تلك الحالات.
</p>

<p>
	نحتاج الآن إلى تحديث تابع <code>content</code> على <code>Post</code>، إذ نريد أن تعتمد القيمة المُعادة من <code>content</code> على حالة <code>Post</code> الحالية، لذلك نعرّف <code>Post</code> مفوض للتابع <code>content</code> المعرّف على قيمة الحقل <code>state</code> الخاص به كما هو موضح في الشيفرة 17.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5411_20" style=""><span class="pln">impl </span><span class="typ">Post</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
    pub fn content</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">str </span><span class="pun">{</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">state</span><span class="pun">.</span><span class="pln">as_ref</span><span class="pun">().</span><span class="pln">unwrap</span><span class="pun">().</span><span class="pln">content</span><span class="pun">(</span><span class="pln">self</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;">
	[الشيفرة 17: تحديث تابع <code>content</code> على <code>Post</code> للتفويض لتابع <code>content</code> على <code>State</code>]
</p>

<p>
	نظرًا لأن الهدف هو الاحتفاظ بكل هذه القواعد داخل الهياكل التي تنفّذ السمة <code>State</code> فإننا نستدعي تابع <code>content</code> على قيمة <code>state</code> ونمرر نسخة المنشور (في هذه الحالة <code>self</code>) مثل وسيط، ثم نعيد القيمة التي أُعيدَت من استعمال تابع <code>content</code> إلى قيمة <code>state</code>.
</p>

<p>
	نستدعي تابع <code>as_ref</code> على <code>Option</code> لأننا نريد مرجعًا للقيمة داخل <code>Option</code> بدلًا من الحصول على <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>. بما أن <code>state</code> هو <code>&lt;&lt;Option&lt;Box&lt;dyn State</code>، تكون القيمة المُعادة عند استدعاء <code>as_ref</code> هي <code>&lt;&lt;Option&lt;&amp;Box&lt;dyn State</code>. نحصل على خطأ إذا لم نستدعي <code>as_ref</code> لأننا لا نستطيع نقل <code>state</code> من <code>self&amp;</code> المستعارة إلى معامل الدالة.
</p>

<p>
	نستدعي بعد ذلك التابع <code>unwrap</code> الذي نعلم أنه لن يهلع أبدًا لأننا نعلم أن التوابع الموجودة على <code>Post</code> تضمن أن <code>state</code> سيحتوي دائمًا على القيمة <code>Some</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-rust-r1921/" rel="">الاختيار ما بين الماكرو panic!‎ والنوع Result للتعامل مع الأخطاء في لغة Rust</a>، وهي عندما نعلم أن قيمة <code>None</code> غير ممكنة أبدًا على الرغم من أن المصرف غير قادر على فهم ذلك.
</p>

<p>
	سيكون تأثير التحصيل القسري deref coercion في هذه المرحلة ساريًا على كل من <code>&amp;</code> و <code>Box</code> عندما نستدعي <code>content</code> على <code>&lt;Box&lt;dyn State&amp;</code>، لذلك يُستدعى تابع <code>content</code> في النهاية على النوع الذي ينفذ سمة <code>State</code>. هذا يعني أننا بحاجة إلى إضافة <code>content</code> إلى تعريف سمة <code>State</code> وهنا سنضع منطق المحتوى الذي سيُعاد اعتمادًا على الحالة التي لدينا كما هو موضح في الشيفرة 18.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5411_22" style=""><span class="pln">trait </span><span class="typ">State</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
    fn content</span><span class="pun">&lt;</span><span class="str">'a&gt;(&amp;self, post: &amp;'</span><span class="pln">a </span><span class="typ">Post</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">
        </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="com">// --snip--</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Published</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

impl </span><span class="typ">State</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">Published</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
    fn content</span><span class="pun">&lt;</span><span class="str">'a&gt;(&amp;self, post: &amp;'</span><span class="pln">a </span><span class="typ">Post</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">
        </span><span class="pun">&amp;</span><span class="pln">post</span><span class="pun">.</span><span class="pln">content
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 18: إضافة تابع <code>content</code> إلى سمة <code>State</code>]
</p>

<p>
	نضيف تنفيذًا مبدئيًا للتابع <code>content</code> الذي يُعيد شريحة سلسلة نصية فارغة، ويعني هذا أننا لسنا بحاجة إلى تنفيذ <code>content</code> في هيكلي <code>Draft</code> و <code>PendingReview</code>، إذ سيُعيد هيكل <code>Published</code> تعريف التابع <code>content</code> ويعيد القيمة في <code>post.content</code>.
</p>

<p>
	لاحظ أننا نحتاج إلى توصيف لدورة الحياة lifetime على هذا التابع كما ناقشنا سابقًا في الفصل <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>، إذ نأخذ هنا مرجعًا إلى <code>post</code> مثل وسيط ونعيد مرجعًا إلى جزء من <code>post</code> وبالتالي ترتبط دورة حياة المرجع المُعاد بدورة حياة وسيط <code>post</code>.
</p>

<p>
	تعمل الشيفرة 11 الآن كاملةً بعد أن طبّقنا نمط الحالة state pattern مع قواعد سير عمل منشور المدونة. المنطق المتعلق بالقواعد موجود في كائنات الحالة بدلًا من بعثرته في جميع أنحاء <code>Post</code>.
</p>

<p>
	<strong>لماذا لم نستخدم تعدادًا؟</strong>
</p>

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

<h2>
	سلبيات استخدام نمط الحالة
</h2>

<p>
	وضّحنا أن رست قادرة على تنفيذ نمط الحالة كائنية التوجه لتغليف أنواع مختلفة من السلوك التي يجب أن يتمتع بها المنشور في كل حالة. لا تعرف التوابع في <code>Post</code> شيئًا عن السلوكيات المختلفة. تسمح لنا الطريقة التي نظّمنا بها الشيفرة البرمجية -تنفيذ سمة <code>State</code> على الهيكل <code>Published</code>- أن ننظر في مكان واحد فقط لمعرفة الطرق المختلفة التي يمكن أن يتصرف بها المنشور المقبول للنشر.
</p>

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

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

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

<ul>
	<li>
		أضف تابع <code>reject</code> الذي يغيّر شكل حالة المنشور من <code>PendingReview</code> إلى <code>Draft</code>.
	</li>
	<li>
		استدعِ <code>approve</code> مرّتين قبل أن تتغير الحالة إلى <code>Published</code>.
	</li>
	<li>
		اسمح للمستخدمين بإضافة محتوى النص فقط عندما يكون المنشور في حالة <code>Draft</code>. تلميح: اجعل كائن الحالة مسؤولًا عما قد يتغير بشأن المحتوى ولكن ليس مسؤولًا عن تعديل <code>Post</code>.
	</li>
</ul>

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

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

<p>
	تتضمن التكرارات الأخرى عمليات تنفيذ مماثلة لتوابع <code>request_review</code> و <code>approve</code> على <code>Post</code>، يفوّض كلا التابعين تنفيذ التابع ذاته على القيمة في حقل <code>state</code> للقيمة <code>Option</code> وتعيين القيمة الجديدة لحقل <code>state</code> إلى النتيجة. إذا كان لدينا الكثير من التوابع في <code>Post</code> التي اتبعت هذا النمط، فقد نفكر في تعريف ماكرو لإزالة التكرار.
</p>

<p>
	لا نستفيد استفادة كاملة من نقاط قوة رست بقدر الإمكان عبر تنفيذ نمط الحالة تمامًا كما هو معرّف في <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-r1375/" rel="">اللغات البرمجية كائنية التوجه</a> الأخرى. دعنا نلقي نظرةً على بعض التغييرات الممكن إجراؤها على الوحدة المصرفة <code>blog</code>، والتي من شأنها أن تجعل الحالات غير الصالحة والانتقالات transitions أخطاءً تظهر وقت التصريف.
</p>

<h2>
	ترميز الحالات والسلوك مثل أنواع
</h2>

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

<p>
	لننظر إلى الجزء الأول من دالة <code>main</code> في الشيفرة 11.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5411_24" style=""><span class="pln">fn main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let mut post </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Post</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">();</span><span class="pln">

    post</span><span class="pun">.</span><span class="pln">add_text</span><span class="pun">(</span><span class="str">"I ate a salad for lunch today"</span><span class="pun">);</span><span class="pln">
    assert_eq</span><span class="pun">!(</span><span class="str">""</span><span class="pun">,</span><span class="pln"> post</span><span class="pun">.</span><span class="pln">content</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ما زلنا نسمح بإنشاء منشورات جديدة في حالة المسودة باستخدام <code>Post::new</code> والقدرة على إضافة نص إلى محتوى المنشور، ولكن بدلًا من وجود تابع <code>content</code> في مسودة المنشور التي تعيد سلسلةً نصيةً فارغة، سنعمل على تعديلها بحيث لا تحتوي مسودة المنشورات على تابع <code>content</code> إطلاقًا؛ وستحصل بهذه الطريقة على خطأ في المصرف إذا حاولنا الحصول على محتوى مسودة منشور يخبرنا أن التابع غير موجود، ونتيجةً لذلك سيكون من المستحيل بالنسبة لنا عرض محتوى مسودة المنشور عن طريق الخطأ في مرحلة الإنتاج production لأن هذه الشيفرة البرمجية لن تصرف. تُظهر الشيفرة 19 تعريف هيكلي <code>Post</code> و <code>DraftPost</code> إضافةً إلى التوابع الخاصة بكل منهما.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5411_26" style=""><span class="pln">pub </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Post</span><span class="pln"> </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">
</span><span class="pun">}</span><span class="pln">

pub </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">DraftPost</span><span class="pln"> </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">
</span><span class="pun">}</span><span class="pln">

impl </span><span class="typ">Post</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"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">DraftPost</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">DraftPost</span><span class="pln"> </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="kwd">new</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 fn content</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">str </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">content
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

impl </span><span class="typ">DraftPost</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pub fn add_text</span><span class="pun">(&amp;</span><span class="pln">mut self</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">{</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">content</span><span class="pun">.</span><span class="pln">push_str</span><span class="pun">(</span><span class="pln">text</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;">
	[الشيفرة 19: <code>Post</code> مع تابع <code>content</code> و <code>DraftPost</code> بدون تابع <code>content</code>]
</p>

<p>
	يحتوي كل من هيكلي <code>Post</code> و <code>DraftPost</code> على حقل <code>content</code> خاص يحتوي على النص الخاص بمنشور المدونة. لم يعد للهياكل حقل <code>state</code> لأننا ننقل ترميز الحالة إلى أنواع الهياكل، وسيمثل هيكل <code>Post</code> منشورًا قد نُشر وله تابع <code>content</code> يُعيد <code>content</code>.
</p>

<p>
	لا تزال لدينا دالة <code>Post::new</code> ولكن بدلًا من إعادة نسخة من <code>Post</code>، ستُعيد نسخةً من <code>DraftPost</code>، وذلك نظرًا لأن <code>content</code> خاص ولا وجود لأي دوال تُعيد <code>Post</code>، وبالتالي لا يمكن إنشاء نسخة عن <code>Post</code> حاليًا.
</p>

<p>
	يحتوي هيكل <code>DraftPost</code> على تابع <code>add_text</code> لذا يمكننا إضافة نص إلى <code>content</code> كما كان من قبل، لكن لاحظ أن <code>DraftPost</code> لا يحتوي على تابع <code>content</code> معرّف لذا يضمن البرنامج الآن بدء جميع المنشورات مثل مسودات منشورات وعدم إتاحة محتوى مسودات المنشورات للعرض. ستؤدي أي محاولة للتحايل على هذه القيود إلى حدوث خطأ في المصرّف.
</p>

<h2>
	تنفيذ الانتقالات مثل تحولات إلى أنواع مختلفة
</h2>

<p>
	كيف نحصل على منشور قد نُشر؟ نريد فرض القاعدة التي تنص على وجوب مراجعة مسودة المنشور والموافقة عليها قبل نشرها. ينبغي عدم عرض أي منشور في حالة "قيد المراجعة" أي محتوى. لنطبّق هذه القيود عن طريق إضافة هيكل آخر باسم <code>PendingReviewPost</code> وتعريف التابع <code>request_review</code> في <code>DraftPost</code> لإعادة <code>PendingReviewPost</code> وتعريف تابع <code>approve</code> على <code>PendingReviewPost</code> لإعادة <code>Post</code> كما هو موضح في الشيفرة 20.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5411_28" style=""><span class="pln">impl </span><span class="typ">DraftPost</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// --snip--</span><span class="pln">
    pub fn request_review</span><span class="pun">(</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">PendingReviewPost</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">PendingReviewPost</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            content</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><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">PendingReviewPost</span><span class="pln"> </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">
</span><span class="pun">}</span><span class="pln">

impl </span><span class="typ">PendingReviewPost</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pub fn approve</span><span class="pun">(</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">Post</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Post</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            content</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><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 20: هيكل <code>PendingReviewPost</code> مُنشأ عن طريق استدعاء <code>request_review</code> على <code>DraftPost</code> وتابع <code>approve</code> الذي يرجع <code>PendingReviewPost</code> إلى <code>Post</code> منشور]
</p>

<p>
	يأخذ التابعان <code>request_review</code> و <code>approve</code> ملكية <code>self</code> وبالتالي تستخدم نُسَخ <code>DraftPost</code> و <code>PendingReviewPost</code> وتحوّلهما إلى <code>PendingReviewPost</code> و <code>Post</code> منشور published على التوالي، وبهذه الطريقة لن يكون لدينا أي نسخ متبقية من <code>DraftPost</code> بعد أن استدعينا <code>request_review</code> عليها وما إلى ذلك.
</p>

<p>
	لا يحتوي هيكل <code>PendingReviewPost</code> على تابع <code>content</code> معرف عليه لذلك تؤدي محاولة قراءة محتواها إلى حدوث خطأ في المصرّف كما هو الحال مع <code>DraftPost</code>، لأن الطريقة الوحيدة للحصول على نسخة <code>Post</code> قد جرى نشره وله تابع <code>content</code> معرّف هي استدعاء تابع <code>approve</code> على <code>PendingReviewPost</code> والطريقة الوحيدة للحصول على <code>PendingReviewPost</code> هي استدعاء تابع <code>request_review</code> على <code>DraftPost</code>، إذ رمّزنا الآن سير عمل منشور المدونة إلى نظام النوع.
</p>

<p>
	يتعين علينا أيضًا إجراء بعض التغييرات الصغيرة على <code>main</code>، إذ يُعيد التابعان <code>request_review</code> و <code>approve</code> حاليًا نسخًا جديدة بدلًا من تعديل الهيكل الذي استدعيت عليهما، لذلك نحتاج إلى إضافة المزيد من الإسنادات الخفية <code>let post =‎</code> لحفظ الأمثلة المُعادة. لا يمكن أيضًا أن تكون لدينا تأكيدات بسلاسل نصية فارغة حول محتويات المسودة ومنشورات بانتظار المراجعة، فنحن لسنا بحاجتها. لا يمكننا تصريف الشيفرة البرمجية التي تحاول استعمال محتوى المنشورات في تلك الحالات بعد الآن. تظهر الشيفرة البرمجية الجديدة ضمن الدالة <code>main</code> في الشيفرة 21.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5411_30" style=""><span class="pln">use blog</span><span class="pun">::</span><span class="typ">Post</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 post </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Post</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">();</span><span class="pln">

    post</span><span class="pun">.</span><span class="pln">add_text</span><span class="pun">(</span><span class="str">"I ate a salad for lunch today"</span><span class="pun">);</span><span class="pln">

    let post </span><span class="pun">=</span><span class="pln"> post</span><span class="pun">.</span><span class="pln">request_review</span><span class="pun">();</span><span class="pln">

    let post </span><span class="pun">=</span><span class="pln"> post</span><span class="pun">.</span><span class="pln">approve</span><span class="pun">();</span><span class="pln">

    assert_eq</span><span class="pun">!(</span><span class="str">"I ate a salad for lunch today"</span><span class="pun">,</span><span class="pln"> post</span><span class="pun">.</span><span class="pln">content</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 21: تعديل <code>main</code> لاستعمال التنفيذ الجديد لسير عمل منشور مدونة]
</p>

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

<p>
	جرّب المهام المقترحة في بداية المقالة على وحدة <code>blog</code> المصرفة كما هي بعد الشيفرة 21 لمعرفة ما هو رأيك في تصميم هذا الإصدار من الشيفرة البرمجية. لاحظ أن بعض المهام قد تكون مكتملة فعلًا في هذا التصميم.
</p>

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

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch17-00-oop.html" rel="external nofollow">Object-Oriented Programming Features of Rust</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%A3%D9%86%D9%85%D8%A7%D8%B7-patterns-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D8%A7%D8%AA%D9%87%D8%A7-%D9%88%D9%82%D8%A7%D8%A8%D9%84%D9%8A%D8%AA%D9%87%D8%A7-%D9%84%D9%84%D8%AF%D8%AD%D8%B6-refutability-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2109/" rel="">الأنماط Patterns واستخداماتها وقابليتها للدحض Refutability في لغة رست</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-%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%B3%D9%85%D8%A9-object-trait-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2098/" rel="">استخدام كائنات السمة Object Trait في لغة رست</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-oop-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2097/" rel="">البرمجة كائنية التوجه <abbr title="Object-Oriented Programming | البرمجة كائنية التوجه"><abbr title="Object-Oriented Programming | البرمجة كائنية التوجه">OOP</abbr></abbr> في لغة رست</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advanced/%D8%A3%D9%86%D9%85%D8%A7%D8%B7-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A-design-patterns-r498/" rel="">أنماط التصميم البرمجي Design patterns</a>
	</li>
	<li>
		<a href="https://wiki.hsoub.com/Design_Patterns" rel="external">سلسلة أنماط التصميم</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2108</guid><pubDate>Sun, 03 Sep 2023 13:00:00 +0000</pubDate></item><item><title>&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x643;&#x627;&#x626;&#x646;&#x627;&#x62A; &#x627;&#x644;&#x633;&#x645;&#x629; Object Trait &#x641;&#x64A; &#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-%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%B3%D9%85%D8%A9-object-trait-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2098/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_08/---Object-Trait----Rust.png.2e45b9a230456e11f4451def87ed825f.png" /></p>
<p>
	ذكرنا سابقًا في الفصل <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> وما بعده أن أحد قيود الشعاع vector هي تخزينه لعناصر من نوع واحد فقط، وقد أنشأنا حلًا بديلًا فيما بعد في الشيفرة 8 من الفصل <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>، إذ عرّفنا التعداد <code>SpreadsheetCell</code> وداخله متغايرات variants تحتوي على أعداد صحيحة integers وعشرية floats ونص text، وهذا يعني أنه يمكننا تخزين أنواع مختلفة من البيانات في كل خلية مع المحافظة على شعاع يمثل صفًا من الخلايا. يعد هذا حلًا جيدًا عندما تمثّل العناصر القابلة للتبديل مجموعةً ثابتةً من الأنواع التي نعرّفها عند تصريف الشيفرة البرمجية الخاصة بنا.
</p>

<p>
	نريد أحيانًا أن يتمكن مستخدم مكتبتنا من توسيع مجموعة الأنواع الصالحة في حالة معينة، ولإظهار كيف يمكننا تحقيق ذلك سننشئ مثالًا لأداة واجهة المستخدم الرسومية graphical user interface ‎ -أو اختصارًا GUI- التي تتكرر من خلال قائمة من العناصر مستدعيةً تابع <code>draw</code> على كل عنصر لرسمه على الشاشة، وهي تقنية شائعة لأدوات واجهة المستخدم الرسومية. سننشئ وحدة مكتبة مصرّفة library crate تدعى <code>gui</code> تحتوي على هيكل مكتبة لأدوات واجهة المستخدم الرسومية GUI، وقد تتضمن هذه الوحدة المصرّفة بعض الأنواع ليستخدمها الأشخاص، مثل <code>Button</code>، أو <code>TextField</code>، كما سيرغب مستخدمو <code>gui</code> بإنشاء أنواعهم الخاصة التي يمكن رسمها، فعلى سبيل المثال قد يضيف أحد المبرمجين <code>Image</code> وقد يضيف آخر <code>SelectBox</code>.
</p>

<p>
	لن نبرمج كامل مكتبة GUI في هذا المثال لكننا سنبين كيف ستعمل الأجزاء معًا. لا يمكننا في وقت كتابة المكتبة معرفة وتعريف جميع الأنواع التي قد يرغب المبرمجون الآخرون بإنشائها، لكننا نعلم أن <code>gui</code> تحتاج إلى تتبُّع العديد من القيم ومن أنواع مختلفة وتحتاج إلى استدعاء تابع <code>draw</code> على كل من هذه القيم المكتوبة بصورةٍ مختلفة. لا يتطلب الأمر معرفة ماذا سيحدث فعلًا عندما نستدعي تابع <code>draw</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> تحتوي على خاصية التوريث قد نعرّف صنفًا يدعى <code>Component</code> له تابع يسمى <code>draw</code>. ترث الأصناف الأخرى، مثل <code>Button</code>، و <code>Image</code>، و <code>SelectBox</code> من <code>Component</code>، وبالتالي ترث تابع <code>draw</code>. يمكن لكل من الأصناف السابقة إعادة تعريف تابع <code>draw</code> لتعريف سلوكهم المخصص، لكن <a href="https://academy.hsoub.com/programming/general/%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-framework/" rel="">إطار العمل framework</a> قد يتعامل مع جميع الأنواع كما لو كانت نُسخًا instance من <code>Component</code> ويستدعي التابع <code>draw</code> عليها، ولكن بما أن رست لا تحتوي على توريث، فنحن بحاجة إلى طريقة أخرى لهيكلة مكتبة <code>gui</code> للسماح للمستخدمين بتوسيعها بأنواع جديدة.
</p>

<h2>
	تعريف سمة لسلوك مشترك
</h2>

<p>
	سنعرّف سمةً باسم <code>Draw</code> يكون لها تابعٌ واحد يسمى <code>draw</code>، لتنفيذ السلوك الذي نريد من الوحدة المصرّفة <code>gui</code> أن تملكه. بعد ذلك، يمكننا تعريف شعاع يأخذ كائن سمة trait object، الذي يشير إلى نسخة من نوع ينفّذ السمة المحددة لدينا، إضافةً إلى جدول يُستخدم للبحث عن توابع السمات في هذا النوع في وقت التنفيذ. ننشئ كائن سمة عن طريق استخدام نوع من المؤشرات مثل المرجع <code>&amp;</code>، أو المؤشر الذكي <code>Box&lt;T&gt;‎</code>، متبوعًا بالكلمة المفتاحية <code>dyn</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%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> و<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</a> بالكائنات لتمييزها عن كائنات اللغات البرمجية الأخرى؛ إذ تُفصل البيانات الموجودة في البنية أو التعداد في حقول الهيكل والسلوك في كتل <code>impl</code>؛ بينما تُسمّى البيانات والسلوك معًا في اللغات الأخرى غالبًا مثل كائن. مع ذلك، تشبه كائنات السمة إلى حد كبير الكائنات في اللغات الأخرى بمعنى أنها تجمع بين البيانات والسلوك، لكن تختلف كائنات السمة عن الكائنات التقليدية في أنه لا يمكننا إضافة بيانات إلى كائن سمة. لا تعدّ كائنات السمة مفيدةً عمومًا مثل الكائنات في اللغات الأخرى، إذ أن الغرض المحدد منها هو السماح بالتجريد عبر سلوكها المشترك.
</p>

<p>
	توضّح الشيفرة 3 كيفية تعريف سمة تسمى<code>Draw</code> مع تابع واحد يسمى <code>draw</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5223_9" style=""><span class="pln">pub trait </span><span class="typ">Draw</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn draw</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 3: تعريف السمة <code>Draw</code>]
</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-rust-r1935/" rel=""> مقدمة إلى مفهوم الأنواع المعممة Generic Types</a>. إلا أن هناك بعض الأشياء الجديدة: إذ تعرٍّف الشيفرة 4 هيكلًا يدعى <code>Screen</code> يحمل شعاعًا باسم <code>components</code>. هذا الشعاع من النوع <code>Box&lt;dyn Draw&gt;‎</code>، وهو كائن سمة، ويُعدّ بديلًا لأي نوع داخل <code>Box</code> ينفّذ السمة <code>Draw</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5223_11" style=""><span class="pln">pub </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Screen</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pub components</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">Box</span><span class="pun">&lt;</span><span class="pln">dyn </span><span class="typ">Draw</span><span class="pun">&gt;&gt;,</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 4: تعريف هيكل <code>Screen</code> مع حقل <code>components</code> الذي يحمل شعاعًا من كائنات سمة تطبّق السمة <code>Draw</code>]
</p>

<p>
	نعرّف على هيكل <code>Screen</code> تابعًا يدعى <code>run</code> يستدعي التابع <code>draw</code> على كل من <code>components</code> الخاصة به كما هو موضح في الشيفرة 5.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5223_13" style=""><span class="pln">impl </span><span class="typ">Screen</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pub fn run</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">for</span><span class="pln"> component in self</span><span class="pun">.</span><span class="pln">components</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">
            component</span><span class="pun">.</span><span class="pln">draw</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: تابع <code>run</code> على <code>Screen</code> الذي يستدعي تابع <code>draw</code> على كل مكون]
</p>

<p>
	يعمل هذا بصورةٍ مختلفة عن تعريف هيكل يستخدم معامل نوع معمّم generic type مع <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 bounds</a>، إذ لا يمكن استبدال معامل النوع المعمم إلا بنوع واحد صريح في كل مرة، بينما تسمح كائنات السمة بأنواع حقيقية متعددة لتحل مكان كائن السمة وقت التنفيذ. على سبيل المثال، كان من الممكن أن نعرّف هيكل <code>Screen</code> باستخدام نوع معمم وحدود سمة كما في الشيفرة 6.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5223_17" style=""><span class="pln">pub </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Screen</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Draw</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pub components</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vec</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">

impl</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> </span><span class="typ">Screen</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln">
</span><span class="kwd">where</span><span class="pln">
    T</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Draw</span><span class="pun">,</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    pub fn run</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">for</span><span class="pln"> component in self</span><span class="pun">.</span><span class="pln">components</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">
            component</span><span class="pun">.</span><span class="pln">draw</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: تنفيذ بديل لهيكل <code>Screen</code> وتابعه <code>run</code> باستخدام أنواع معممة وحدود السمة]
</p>

<p>
	يقيّدنا هذا بنسخة <code>Screen</code> التي تحتوي على قائمة من المكونات جميعها من النوع <code>Button</code>، أو من النوع <code>TextField</code>؛ فإذا كان لديك مجموعات متجانسة homogeneous collections فقط، يُفضّل استعمال أنواع معممة وحدود السمات لأن التعريفات ستكون أحادية الشكل monomorphized في وقت التصريف لاستخدام الأنواع الفعلية.
</p>

<p>
	من جهة أخرى، يمكن لنسخة <code>Screen</code> واحدة أن تحمل النوع <code>Vec&lt;T&gt;‎</code> باستخدام التابع الذي يستخدم كائنات السمة، الذي يحتوي بدوره على <code>&lt;Box&lt;Button</code>، إضافةً إلى <code>&lt;Box&lt;TextField</code>. لنلقي نظرةً على كيفية عمل ذلك، ثم سنتحدث عن الآثار المترتبة على وقت التنفيذ.
</p>

<h2>
	تنفيذ السمة
</h2>

<p>
	سنضيف الآن بعض الأنواع التي تنفّذ السمة <code>Draw</code>، وسنأخذ النوع <code>Button</code> مثالًا على هذه الأنواع. يُعد تنفيذ مكتبة GUI كما ذكرنا سابقًا خارج موضوعنا هنا، لذا لن يكون لتابع <code>draw</code> أي تنفيذ فعلي داخله. لنتخيل الشكل الذي قد يبدو عليه التنفيذ، فقد يحتوي هيكل <code>Button</code> على حقول لكل من <code>width</code> و <code>height</code> و <code>label</code> كما هو موضح في الشيفرة 7.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5223_19" style=""><span class="pln">pub </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Button</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pub width</span><span class="pun">:</span><span class="pln"> u32</span><span class="pun">,</span><span class="pln">
    pub height</span><span class="pun">:</span><span class="pln"> u32</span><span class="pun">,</span><span class="pln">
    pub label</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">Draw</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">Button</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn draw</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="com">// الشيفرة البرمجية المسؤولة عن رسم الزر</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>Button</code> الذي بطبّق السمة <code>Draw</code>]
</p>

<p>
	ستختلف حقول <code>width</code> و <code>height</code> و <code>label</code> الموجودة في <code>Button</code> عن الحقول الموجودة في المكونات الأخرى، فعلى سبيل المثال قد يحتوي النوع <code>TextField</code> نفس الحقول، إضافةً إلى حقل <code>placeholder</code>. ستنفّذ كل الأنواع التي نريد رسمها على الشاشة سمة <code>Draw</code>، لكن ستستعمل شيفرةً برمجيةً مختلفة في التابع <code>draw</code> لتعريف كيفية رسم ذلك النوع تحديدًا، كما في <code>Button</code> هنا (بدون شيفرة برمجية لمكتبة GUI فعلية كما ذكرنا سابقًا). قد يحتوي النوع <code>Button</code> على سبيل المثال كتلة <code>impl</code> إضافية تحتوي على توابع مرتبطة بما يحدث عندما يضغط مستخدم الزر، ولا تنطبق هذه الأنواع من التوابع على أنواع مثل <code>TextField</code>.
</p>

<p>
	إذا قرر شخص ما استعمال مكتبتنا لتطبيق هيكل <code>SelectBox</code> الذي يحتوي الحقول<code>width</code> و <code>height</code> و <code>options</code>، فسينفّذ سمة <code>Draw</code> على النوع <code>SelectBox</code> أيضًا كما هو موضح بالشيفرة 8.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5223_21" style=""><span class="pln">use gui</span><span class="pun">::</span><span class="typ">Draw</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">SelectBox</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">
    options</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">

impl </span><span class="typ">Draw</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">SelectBox</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn draw</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="com">// الشيفرة البرمجية المسؤولة عن رسم صندوق الاختيار</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>gui</code> وتنفذ سمة <code>Draw</code> على هيكل <code>SelectBox</code>]
</p>

<p>
	يمكن لمستخدم مكتبتنا الآن كتابة الدالة <code>main</code> ليُنشئ نسخةً من <code>Screen</code>، ثم إضافة كل من <code>SelectBox</code> و <code>Button</code> لنسخة <code>Screen</code> بوضع كل واحدة منها في <code>&lt;Box&lt;T</code> لتصبح سمة كائن، ويمكنه بعد ذلك استدعاء التابع <code>run</code> على نسخة<code>Screen</code> التي ستستدعي <code>draw</code> على كل من المكونات، وتوضح الشيفرة 9 التطبيق المذكور.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5223_23" style=""><span class="pln">use gui</span><span class="pun">::{</span><span class="typ">Button</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Screen</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 screen </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Screen</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        components</span><span class="pun">:</span><span class="pln"> vec</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">SelectBox</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">75</span><span class="pun">,</span><span class="pln">
                height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln">
                options</span><span class="pun">:</span><span class="pln"> vec</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">"Yes"</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">"Maybe"</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">"No"</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">Box</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Button</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">50</span><span class="pun">,</span><span class="pln">
                height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln">
                label</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">"OK"</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">

    screen</span><span class="pun">.</span><span class="pln">run</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 9: استخدام كائنات السمة لتخزين قيم لأنواع مختلفة تنفذ السمة ذاتها]
</p>

<p>
	لم نفترض عند كتابتنا للمكتبة بأن شخصًا ما قد يضيف النوع <code>SelectBox</code>، إلا أن تطبيق <code>Screen</code> لدينا قادرٌ على العمل مع النوع الجديد ورسمه، وذلك لأن <code>SelectBox</code> ينفّذ سمة <code>Draw</code>، ما يعني أنه ينفّذ تابع <code>draw</code>.
</p>

<p>
	هذا المفهوم - المتمثل بالاهتمام فقط بالرسائل التي تستجيب لها القيمة بدلًا من النوع الحقيقي للقيمة - مشابهٌ لمفهوم كتابة البطة duck typing في اللغات البرمجية المكتوبة ديناميكيًا؛ بمعنى أنه إذا كان شيء ما يسير مثل البطة ويصدر صوتًا مثل البطة، فيجب أن يكون بطة لا محالة. لا يحتاج <code>run</code> إلى معرفة النوع الحقيقي لكل مكون عند تنفيذ <code>run</code> على <code>Screen</code> في الشيفرة 5، فهو لا يتحقق ما إذا كان المكوِّن نسخةً من النوع <code>Button</code> أو <code>SelectBox</code> بل يستدعي فقط التابع <code>draw</code> على المكوّن. عرّفنا <code>Screen</code> لتحتاج إلى قيم يمكننا استدعاء تابع <code>draw</code> عليها من خلال تحديد <code>&lt;Box&lt;dyn Draw</code> على أنه نوع القيم في الشعاع <code>components</code>.
</p>

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

<p>
	تُظهر الشيفرة 10 على سبيل المثال ما يحدث إذا حاولنا إنشاء <code>Screen</code> مع <code>String</code> مثل مكوِّن:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5223_25" style=""><span class="pln">use gui</span><span class="pun">::</span><span class="typ">Screen</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 screen </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Screen</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        components</span><span class="pun">:</span><span class="pln"> vec</span><span class="pun">![</span><span class="typ">Box</span><span class="pun">::</span><span class="kwd">new</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">"Hi"</span><span class="pun">))],</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">

    screen</span><span class="pun">.</span><span class="pln">run</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 10: محاولة استخدام نوع لا ينفّذ سمة كائن السمة]
</p>

<p>
	سنحصل على الخطأ التالي، وذلك لأن <code>String</code> لا ينفّذ السمة <code>Draw</code>:
</p>

<pre class="ipsCode">$ cargo run
   Compiling gui v0.1.0 (file:///projects/gui)
error[E0277]: the trait bound `String: Draw` is not satisfied
 --&gt; src/main.rs:5:26
  |
5 |         components: vec![Box::new(String::from("Hi"))],
  |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Draw` is not implemented for `String`
  |
  = help: the trait `Draw` is implemented for `Button`
  = note: required for the cast from `String` to the object type `dyn Draw`

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

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

<h2>
	الإرسال الديناميكي لكائنات السمة
</h2>

<p>
	باستذكار حديثنا عن عملية توحيد الشكل monomorphization المنفذة بواسطة المصرف عندما نستخدم حدود السمة على الأنواع المعممّة وذلك في قسم (أداء الشيفرة باستعمال الأنواع المعممة) في الفصل <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="">كيفية استخدام أنواع البيانات المعممة Generic Data Types</a>: يولّد المصرّف تطبيقات غير معممّة للدوال والتوابع لكل نوع حقيقي نستخدمه بدلًا من معامل نوع مُحدّد. تنجز الشيفرة البرمجية الناتجة من عملية توحيد الشكل إيفادًا ساكنًا static dispatch، والذي يحدث عندما يعرِف المصرّف التابع الذي تستدعيه وقت التصريف. يتعارض هذا مع الإيفاد الديناميكي الذي يحدث عندما يتعذر على المصرف أن يخبرك بالتابع الذي تستدعيه وقت التصريف. يرسل المصرّف في حالات الإيفاد الديناميكي شيفرة برمجية تُحدّد في وقت التنفيذ التابع الذي يجب استدعاؤه.
</p>

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

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch17-00-oop.html" rel="external nofollow">Object-Oriented Programming Features of Rust</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%D9%81%D9%8A%D8%B0-%D9%86%D9%85%D8%B7-%D8%AA%D8%B5%D9%85%D9%8A%D9%85%D9%8A-design-pattern-%D9%83%D8%A7%D8%A6%D9%86%D9%8A-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-object-oriented-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2108/" rel="">تنفيذ نمط تصميمي Design Pattern كائني التوجه Object-Oriented في لغة رست</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-oop-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2097/" rel="">البرمجة كائنية التوجه <abbr title="Object-Oriented Programming | البرمجة كائنية التوجه"><abbr title="Object-Oriented Programming | البرمجة كائنية التوجه">OOP</abbr></abbr> في لغة رست</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</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 في لغة رست</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2098</guid><pubDate>Thu, 31 Aug 2023 13:03:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629; &#x643;&#x627;&#x626;&#x646;&#x64A;&#x629; &#x627;&#x644;&#x62A;&#x648;&#x62C;&#x647; OOP &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A; Rust</title><link>https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-oop-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2097/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_08/---OOP----Rust.png.069d0b45a0f37acd56ff5d6ae1bf169e.png" /></p>
<p>
	لا يوجد إجماع في مجتمع البرمجة حول الميزات التي يجب أن تكون موجودة في لغة البرمجة حتى تكون لغة كائنية التوجه، وتتأثر رست بالعديد من <a href="https://wiki.hsoub.com/Design_Patterns" rel="external">نماذج البرمجة programming paradigms</a> بما في ذلك البرمجة كائنية التوجه، إذ اكتشفنا الميزات التي جاءت من <a href="https://academy.hsoub.com/programming/general/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D9%88%D8%B8%D9%8A%D9%81%D9%8A%D8%A9-functional-programming-r1391/" rel="">البرمجة الوظيفية functional programming</a> سابقًا بدءًا من الفصل <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</a>. يمكن القول أن اللغات كائنية التوجه تشترك ببعض الميزات المشتركة وهي الكائنات objects والتغليف encapsulation والوراثة inheritance، لنلقي نظرةً على ما تعنيه كل من هذه الميزات وما إذا كانت رست تدعمها.
</p>

<h2>
	الكائنات واحتوائها على بيانات وسلوك
</h2>

<p>
	يُعدّ الكتاب "أنماط التصميم: عناصر البرمجيات الموجهة للكائنات القابلة لإعادة الاستعمال Design Patterns: Elements of "Reusable Object-Oriented Software للمؤلفين إريك جاما Erich Gamma،وريتشارد هيلم Richard Helm، ورالف جونسون Ralph Johnson، وجون فليسيديس John Vlissides، التابع لدار النشر (Addison-Wesley Professional, 1994)، والذي يشار إليه بالعامية كتاب عصابة الأربعة The Gang of Four book، فهرسًا لأنماط التصميم كائنية التوجه، ويعرّف الكتاب البرمجة كائنية التوجّه بهذه الطريقة:
</p>

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

	<p data-gramm="false">
		تتكون البرامج كائنية التوجه من كائنات، ويضمّ الكائن داخله كل من البيانات والإجراءات procedures التي تستخدم تلك البيانات، وتدعى الإجراءات عادًة بالتوابع methods أو العمليات operations.
	</p>
</blockquote>

<p>
	بالنظر للتعريف السابق تكون رست لغة كائنية التوجه؛ إذ تحتوي الهياكل structs والتعدادات enums على البيانات، وتقدّم كتل <code>impl</code> توابعًا على الهياكل والتعدادات، وعلى الرغم من أن الهياكل والتعدادات ذات التوابع لا تدعى بالكائنات إلا أنها تقدّم الوظيفة نفسها وذلك بحسب تعريف الكائنات في كتاب عصابة الأربعة.
</p>

<p>
	ويمكنك الرجوع إلى توثيق <a href="https://wiki.hsoub.com/Design_Patterns" rel="external">أنماط التصميم</a> العربي في موسوعة حسوب لمزيد من التفاصيل.
</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%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="">واجهة برمجية</a> عامة Public <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> خاصة به، وينبغي ألا تكون الشيفرة البرمجية التي يستخدمها الكائن قادرةً على الوصول إلى الأجزاء الداخلية للكائن وتغيير البيانات أو السلوك مباشرةً، وهذا يمكّن المبرمج من تغيير وإعادة تشكيل العناصر الداخلية للكائن دون الحاجة إلى تغيير الشيفرة البرمجية التي تستخدم الكائن.
</p>

<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="">الحزم packages والوحدات المصرفة crates</a>، إذ يمكننا استخدام الكلمة المفتاحية <code>pub</code> لتحديد أي من الوحدات modules والأنواع types والدوال functions والتوابع methods في الشيفرات البرمجية الخاصة بنا التي ينبغي أن تكون عامة، ويكون كل شيء آخر خاص افتراضيًا، فعلى سبيل المثال يمكننا تعريف هيكل <code>AveragedCollection</code> يحتوي على حقل يضمّ شعاعًا vector بقيم <code>i32</code>، كما يمكن للهيكل أيضًا أن يحتوي على حقل يضمّ متوسط القيم في الشعاع مما يعني أنه لا لزوم لحساب المتوسط عند الطلب كلما احتاجه أي أحد. بعبارة أخرى سيخزّن <code>AveragedCollection</code> المتوسط الناتج. تحتوي الشيفرة 1 على تعريف لهيكل <code>AveragedCollection</code>:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3389_8" style=""><span class="pln">pub </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">AveragedCollection</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"> </span><span class="typ">Vec</span><span class="str">&lt;i32&gt;</span><span class="pun">,</span><span class="pln">
    average</span><span class="pun">:</span><span class="pln"> f64</span><span class="pun">,</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 1: هيكل <code>AveragedCollection</code> الذي يخزّن قائمة من الأعداد الصحيحة والمتوسط لعناصر التجميعة]
</p>

<p>
	الهيكل مُشار إليه بالكلمة المفتاحية <code>pub</code>، بحيث يمكن لشيفرة برمجية أخرى استخدام ذلك الهيكل، إلا أن الحقول الموجودة داخل الهيكل تبقى خاصة. هذا مهمٌ في هذه الحالة لأننا نريد التأكد من أنه كلما أُضيفت قيمة أو أُزيلت من الشيفرة يُحَدَّث المتوسط أيضًا، ونحقق ذلك من خلال تطبيق توابع <code>add</code> و <code>remove</code> و <code>average</code> كما هو موضح في الشيفرة 2:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3389_10" style=""><span class="pln">impl </span><span class="typ">AveragedCollection</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pub fn add</span><span class="pun">(&amp;</span><span class="pln">mut self</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">
        self</span><span class="pun">.</span><span class="typ">list</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">
        self</span><span class="pun">.</span><span class="pln">update_average</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    pub fn remove</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="str">&lt;i32&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let result </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="typ">list</span><span class="pun">.</span><span class="pln">pop</span><span class="pun">();</span><span class="pln">
        match result </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">value</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                self</span><span class="pun">.</span><span class="pln">update_average</span><span class="pun">();</span><span class="pln">
                </span><span class="typ">Some</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">None</span><span class="pln"> </span><span class="pun">=&gt;</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="pun">}</span><span class="pln">

    pub fn average</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"> f64 </span><span class="pun">{</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">average
    </span><span class="pun">}</span><span class="pln">

    fn update_average</span><span class="pun">(&amp;</span><span class="pln">mut self</span><span class="pun">)</span><span class="pln"> </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"> self</span><span class="pun">.</span><span class="typ">list</span><span class="pun">.</span><span class="pln">iter</span><span class="pun">().</span><span class="pln">sum</span><span class="pun">();</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">average </span><span class="pun">=</span><span class="pln"> total as f64 </span><span class="pun">/</span><span class="pln"> self</span><span class="pun">.</span><span class="typ">list</span><span class="pun">.</span><span class="pln">len</span><span class="pun">()</span><span class="pln"> as f64</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;">
	[الشيفرة 2: تطبيق التوابع العامة <code>add</code> و<code>remove</code> و<code>average</code> على <code>AveragedCollection</code>]
</p>

<p>
	تعدّ التوابع العامة <code>add</code> و <code>remove</code> و <code>average</code> الوسائل الوحيدة للوصول إلى البيانات أو تعديلها في نسخ من <code>AveragedCollection</code>. يُستدعى التابع الخاص <code>update_average</code> عندما يضاف عنصر على <code>list</code> باستخدام التابع <code>add</code> أو يُزال باستخدام التابع <code>remove</code>، وهو التابع الذي يحدّث حقل <code>average</code> بدوره.
</p>

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

<p>
	بما أننا غلفنا تفاصيل تنفيذ الهيكل <code>AveragedCollection</code>، يمكننا بسهولة مستقبلًا تغيير بعض التفاصيل مثل هيكل البيانات، فعلى سبيل المثال، يمكننا استعمال <code>&lt;HashSet&lt;i32</code> بدلًا من <code>&lt;Vec&lt;i32</code> لحقل <code>list</code>. بما أن بصمات التوابع العامة <code>add</code> و <code>remove</code> و <code>average</code> بقيت على حالها، فلا ضرورة للتعديل على الشيفرة البرمجية التي تستخدم <code>AveragedCollection</code>. ولكنّ هذا الأمر لن يكون محققًا إذا جعلنا <code>list</code> عامة بدلًا من ذلك، إذ تملك <code>&lt;HashSet&lt;i32</code> و <code>&lt;Vec&lt;i32</code> توابعًا مختلفة لإضافة وإزالة العناصر بحيث ستحتاج إلى تغيير الشيفرة البرمجية الخارجية غالبًا في حال كانت تعدل على <code>list</code> مباشرةً.
</p>

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

<h2>
	الوراثة واستخدامها مثل نظام نوع ومشاركة الشيفرة البرمجية
</h2>

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

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

<p>
	قد تحتاج للتوريث لسببين رئيسيين؛ أحدهما لإعادة استعمال الشيفرة البرمجية، ويمكنك في هذه الحالة تنفيذ سلوك معين لنوع واحد، ويمكّنُك التوريث بدوره من إعادة استعمال هذا التنفيذ لنوع مختلف. يمكنك استخدام هذه الوسيلة بطريقة محدودة في شيفرة رست باستخدام تطبيقات تابع السمة الافتراضية التي رأيتها في الشيفرة 14 من فصل <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> عندما أضفنا تنفيذًا افتراضيًا لتابع <code>summarize</code> على السمة <code>Summary</code>. سيُتاح لأي نوع يطبق السمة <code>Summary</code> التابع <code>summarize</code> دون أي شيفرة برمجية إضافية، وهذا مشابه للصنف الأب parent class الذي يحتوي على تنفيذ لتابع وصنف ابن يرث الصف الأب ويحتوي على تنفيذ التابع. يمكننا أيضًا تجاوز التطبيق الافتراضي لتابع <code>summarize</code> عندما نطبق السمة <code>Summary</code> التي تشبه الصنف الابن الذي يُعيد تعريف تابع موروث من صنف أب.
</p>

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

<h3>
	التعددية الشكلية Polymorphism
</h3>

<p>
	ينظر الكثير من الناس إلى التعددية الشكلية polymorphism بكونها مشابهة للوراثة لكنها فعليًا مفهوم أوسع، إذ تشير إلى الشيفرة البرمجية التي يمكنها العمل مع بيانات ذات أنواع مختلفة، بينما تكون هذه الأنواع بالنسبة للوراثة أصنافًا فرعيةً subclass عمومًا.
</p>

<p>
	يستخدم رست أنواع معممة generics بدلًا من هذا لتجريد الأنواع المختلفة الممكنة وحدود السمات trait bounds، وذلك لفرض قيود على ما يجب أن توفره هذه الأنواع، إذ يسمى ذلك أحيانًا بالتعددية الشكلية المحدودة المقيّدة bounded parametric polymorphism.
</p>

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

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

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch17-00-oop.html" rel="external nofollow">Object-Oriented Programming Features of Rust</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-%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%B3%D9%85%D8%A9-object-trait-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2098/" rel="">استخدام كائنات السمة Object Trait في لغة رست</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D8%A7%D9%84%D8%AD%D8%A7%D9%84%D8%A9-%D8%A7%D9%84%D9%85%D8%B4%D8%AA%D8%B1%D9%83%D8%A9-shared-state-concurrency-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D9%88%D8%AA%D9%88%D8%B3%D9%8A%D8%B9-%D8%A7%D9%84%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D9%85%D8%B9-send-%D9%88-sync-r2089/" rel="">تزامن الحالة المشتركة Shared-State Concurrency في لغة رست وتوسيع التزامن مع Send و Sync</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-r1375/" rel="">البرمجة كائنية التوجه</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/questions/16110-%D9%85%D8%A7%D9%87%D9%88-polymorphism/" rel="">ما هي التعددية الشكلية polymorphism؟</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%A7%D9%84%D8%AA%D8%B9%D8%AF%D8%AF%D9%8A%D8%A9-%D8%A7%D9%84%D8%B4%D9%83%D9%84%D9%8A%D8%A9-polymorphism-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r757/" rel="">كيفية تطبيق التعددية الشكلية (Polymorphism) على الأصناف في بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D9%88%D8%B8%D9%8A%D9%81%D9%8A%D8%A9-functional-programming-r1391/" rel="">مقدمة إلى البرمجة الوظيفية Functional Programming</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2097</guid><pubDate>Sat, 26 Aug 2023 13:00:00 +0000</pubDate></item><item><title>&#x62A;&#x632;&#x627;&#x645;&#x646; &#x627;&#x644;&#x62D;&#x627;&#x644;&#x629; &#x627;&#x644;&#x645;&#x634;&#x62A;&#x631;&#x643;&#x629; Shared-State Concurrency &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A; &#x648;&#x62A;&#x648;&#x633;&#x64A;&#x639; &#x627;&#x644;&#x62A;&#x632;&#x627;&#x645;&#x646; &#x645;&#x639; Send &#x648; Sync</title><link>https://academy.hsoub.com/programming/rust/%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D8%A7%D9%84%D8%AD%D8%A7%D9%84%D8%A9-%D8%A7%D9%84%D9%85%D8%B4%D8%AA%D8%B1%D9%83%D8%A9-shared-state-concurrency-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D9%88%D8%AA%D9%88%D8%B3%D9%8A%D8%B9-%D8%A7%D9%84%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D9%85%D8%B9-send-%D9%88-sync-r2089/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_08/---Shared-State-Concurrency----Rust--Send-Sync.png.16e492bbea4226c40c3d6bc56e380213.png" /></p>
<p>
	يُعدّ تمرير الرسائل طريقةً جيدةً للتعامل مع التزامن ولكنها ليست الطريقة الوحيدة، إذ أن هناك طريقةٌ أخرى لوصول خيوط threads متعددة إلى ذات بيانات المُشاركة. ضع بالحسبان هذا الجزء من الشعار من توثيق لغة جو Go "لا تتواصل بمشاركة الذاكرة".
</p>

<p>
	كيف سيبدو التواصل من خلال مشاركة الذاكرة؟ وبالإضافة إلى ذلك، لماذا يحذر المدافعون عن تمرير الرسائل message-passing من استخدام مشاركة الذاكرة memory sharing؟
</p>

<p>
	تشبه القنوات channels في أي لغة برمجة -بطريقة ما- الملكية الفردية لأنه بمجرد نقلك لقيمة ما إلى قناة يجب ألا تستخدم هذه القيمة بعدها. يشبه تزامن الذاكرة المشتركة الملكية المتعددة multiple ownership، إذ يمكن للخيوط المتعددة أن تصل إلى موقع الذاكرة ذاته في الوقت نفسه. كما رأينا سابقًا في مقال <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 في رست</a>، فقد جعلت المؤشرات الذكية smart pointers الملكية المتعددة ممكنة، ويمكن للملكية المتعددة أن تعقد الأمر لأن الملّاك المختلفين بحاجة إلى إدارة. يساعد نظام رست وقواعد الملكية الخاصة به كثيرًا في جعل عملية الإدارة صحيحة. لنلقي على سبيل المثال نظرةً على كائنات المزامنة mutexes التي تعدّ واحدةً من أكثر بدائل التزامن شيوعًا للذاكرة المشتركة.
</p>

<h2>
	استعمال كائنات المزامنة للسماح بالوصول للبيانات عن طريق خيط واحد بالوقت ذاته
</h2>

<p>
	كائن المزامنة Mutex هو اختصارٌ للاستبعاد المتبادل mutual exclusion، أي يسمح كائن المزامنة لخيط واحد فقط أن يصل إلى بعض البيانات في أي وقت، وللوصول إلى البيانات في كائن المزامنة يجب أن يشير الخيط أولًا إلى أنه يريد الوصول عن طريق طلب الحصول على قفل كائن المزامنة mutex's lock؛ والقفل هو هيكل بيانات <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 structure</a> يعد جزءًا من كائن المزامنة الذي يتتبع من لديه حاليًا وصولٌ حصري إلى البيانات، وبالتالي يُوصَف كائن المزامنة بأنه يحمي البيانات التي يحملها عبر نظام القفل.
</p>

<p>
	لكائنات المزامنة سمعة بأنها صعبة الاستعمال لأنك يجب أن تتذكر قاعدتين:
</p>

<ul>
	<li>
		يجب أن تحاول الحصول على القفل قبل استعمال البيانات.
	</li>
	<li>
		يجب أن تلغي قفل البيانات عندما تنتهي من البيانات التي يحميها كائن المزامنة حتى يتسنّى للخيوط الأخرى الحصول على القفل.
	</li>
</ul>

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

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

<h3>
	واجهة &lt;Mutex&lt;T البرمجية
</h3>

<p>
	لنبدأ باستعمال كائن مزامنة بسياق خيط وحيد single-threaded مثالًا عن كيفية استعمال كائن مزامنة كما هو موضح في الشيفرة 12:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7084_8" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">sync</span><span class="pun">::</span><span class="typ">Mutex</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 m </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Mutex</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">

    </span><span class="pun">{</span><span class="pln">
        let mut num </span><span class="pun">=</span><span class="pln"> m</span><span class="pun">.</span><span class="pln">lock</span><span class="pun">().</span><span class="pln">unwrap</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"> </span><span class="lit">6</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">"m = {:?}"</span><span class="pun">,</span><span class="pln"> m</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 12: تجربة واجهة <code>&lt;Mutex&lt;T</code> البرمجية بسياق خيط وحيد لبساطته
</p>

<p>
	كما هو الحال مع العديد من الأنواع نُنشئ <code>&lt;Mutex&lt;T</code> باستعمال الدالة المرتبطة <code>‫new</code>، ونستخدم التابع <code>lock</code> للوصول إلى البيانات داخل كائن المزامنة وذلك للحصول على القفل. سيحظر هذا الاستدعاء الخيط الحالي ولن تتمكن من فعل أي عمل حتى يحين دور الحصول على القفل.
</p>

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

<p>
	يمكننا أن نعالج القيمة التي حصلنا عليها التي تحمل الاسم <code>num</code> بعد حصولنا على القفل وستكون في هذه الحالة بمثابة مرجع متغير mutable يُشير إلى البيانات الموجودة بالداخل. يضمن نظام النوع type system حصولنا على قفل قبل استخدام القيمة في <code>m</code>، ونوع <code>m</code> هو <code>&lt;Mutex&lt;i32</code> وليس <code>i32</code> لذلك يجب علينا استدعاء <code>lock</code> حتى نتمكن من استخدام القيمة <code>i32</code>، ودعنا لا ننسى أن نظام النوع لن يسمح لنا بالوصول إلى قيمة <code>i32</code> الداخلية بخلاف ذلك.
</p>

<p>
	كما تعتقد فإن <code>&lt;Mutex&lt;T</code> هو مؤشر ذكي، وبدقة أكبر، يُعيد استدعاء <code>lock</code> مؤشرًا ذكيًا يدعى <code>MutexGuard</code> مُغلَّفًا في <code>LockResult</code> الذي تعاملنا معه في استدعاء <code>unwrap</code>، يُطبّق المؤشر الذكي <code>MutexGuard</code> السمة <code>Deref</code> ليشير إلى بياناتنا الداخلية، كما يحتوي المؤشر الذكي أيضًا على تطبيق للسمة <code>Drop</code> يُحرّر القفل تلقائيًا عندما يخرج <code>MutexGuard</code> عن النطاق وهو الأمر الذي يحدث بنهاية النطاق الداخلي. نتيجة لذلك لا نخاطر بنسيان تحرير القفل وحجب استخدام كائن المزامنة بواسطة الخيوط الأخرى لأن تحرير القفل يحدث تلقائيًا.
</p>

<p>
	يمكننا طباعة قيمة كائن المزامنة بعد تحرير القفل وسنرى أننا تمكنا من تغيير القيمة ذات النوع<code>i32</code> الداخلية إلى 6.
</p>

<h3>
	مشاركة &lt;Mutex&lt;T بين خيوط متعددة
</h3>

<p>
	دعنا نحاول الآن مشاركة قيمة بين خيوط متعددة باستخدام <code>&lt;Mutex&lt;T</code>، سنمرّ على عشرة خيوط ونجعل كل خيط منها يزيد قيمة العداد بمقدار 1 بحيث ينتقل العداد من القيمة 0 إلى 10. يحتوي المثال التالي في الشيفرة 13 على خطأ تصريفي compiler error، وسنستفيد من هذا الخطأ حتى نتعلم المزيد حول استعمال <code>&lt;Mutex&lt;T</code> وكيف سيساعدنا رست في استعماله بصورةٍ صحيحة.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7084_10" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">sync</span><span class="pun">::</span><span class="typ">Mutex</span><span class="pun">;</span><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 counter </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Mutex</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln">
    let mut handles </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![];</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> _ in </span><span class="lit">0.</span><span class="pun">.</span><span class="lit">10</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let handle </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"> </span><span class="pun">{</span><span class="pln">
            let mut num </span><span class="pun">=</span><span class="pln"> counter</span><span class="pun">.</span><span class="pln">lock</span><span class="pun">().</span><span class="pln">unwrap</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"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">});</span><span class="pln">
        handles</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">handle</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"> handle in handles </span><span class="pun">{</span><span class="pln">
        handle</span><span class="pun">.</span><span class="pln">join</span><span class="pun">().</span><span class="pln">unwrap</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">"Result: {}"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">counter</span><span class="pun">.</span><span class="pln">lock</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;">
	الشيفرة 13: عشرة خيوط يزيد كل واحد منها عداد محميّ باستخدام <code>&lt;Mutex &lt;T</code>
</p>

<p>
	ننشئ متغيرًا ندعوه <code>counter</code> ليحمل قيمة من النوع <code>i32</code> داخل <code>&lt;Mutex&lt;T</code> كما فعلنا في الشيفرة 12، بعد ذلك نُنشئ عشرة خيوط عبر المرور iterate على مجال من الأرقام، ونستخدم لتحقيق ذلك <code>thread::spawn</code> ونمنح كل خيط المغلّف ذاته الذي ينقل العداد باتجاه الخيط ويحصل على قفل على <code>&lt;Mutex&lt;T</code> عن طريق استدعاء التابع <code>lock</code> ومن ثم يضيف القيمة 1 إلى القيمة الموجودة في كائن المزامنة. عندما ينتهي الخيط من تنفيذ مغلّفه يخرج <code>num</code> عن النطاق ويحرر القفل بحيث يستطيع خيطٌ آخر الحصول عليه.
</p>

<p>
	نجمع كل مقابض الانضمام join handles في الخيط الرئيسي، ونستدعي بعد ذلك -<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%AE%D9%8A%D9%88%D8%B7-threads-%D9%84%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%B1%D8%B3%D8%AA-%D8%A8%D8%B5%D9%88%D8%B1%D8%A9-%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D8%A2%D9%86%D9%8A%D9%8B%D8%A7-r2043/" rel="">كما فعلنا في الشيفرة 2 سابقًا</a>- <code>join</code> على كل مقبض للتأكد من انتهاء جميع الخيوط، وعند هذه النقطة يحصل الخيط الرئيسي على القفل ويطبع نتيجة هذا البرنامج.
</p>

<p>
	لمّحنا إلى أن هذا المثال لن يُصرَّف، لنتعرف على السبب الآن:
</p>

<pre class="ipsCode">$ cargo run
   Compiling shared-state v0.1.0 (file:///projects/shared-state)
error[E0382]: use of moved value: `counter`
  --&gt; src/main.rs:9:36
   |
5  |     let counter = Mutex::new(0);
   |         ------- move occurs because `counter` has type `Mutex&lt;i32&gt;`, which does not implement the `Copy` trait
...
9  |         let handle = thread::spawn(move || {
   |                                    ^^^^^^^ value moved into closure here, in previous iteration of loop
10 |             let mut num = counter.lock().unwrap();
   |                           ------- use occurs due to use in closure

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

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

<h3>
	ملكية متعددة مع خيوط متعددة
</h3>

<p>
	تكلمنا <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1-rc%E2%80%8E-%D8%A7%D9%84%D8%B0%D9%83%D9%8A-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D9%87-%D9%84%D9%84%D8%A5%D8%B4%D8%A7%D8%B1%D8%A9-%D8%A5%D9%84%D9%89-%D8%B9%D8%AF%D8%AF-%D8%A7%D9%84%D9%85%D8%B1%D8%A7%D8%AC%D8%B9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2036/" rel="">في مقال سابق</a> عن المالكين المتعددين لقيمة باستخدام المؤشر الذكي <code>&lt;Rc&lt;T </code><t>لإنشاء قيمة مرجعية معدودة، لننفّذ الشيء ذاته هنا ونلاحظ النتيجة. نغلّف <code>&lt;Mutex&lt;T</code> داخل <code>&lt;Rc&lt;T</code> ضمن الشيفرة 14 ونستنسخ <code>&lt;Rc&lt;T</code> قبل نقل الملكية إلى الخيط.</t>
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7084_12" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">rc</span><span class="pun">::</span><span class="typ">Rc</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">sync</span><span class="pun">::</span><span class="typ">Mutex</span><span class="pun">;</span><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 counter </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Mutex</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="lit">0</span><span class="pun">));</span><span class="pln">
    let mut handles </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![];</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> _ in </span><span class="lit">0.</span><span class="pun">.</span><span class="lit">10</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let counter </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">clone</span><span class="pun">(&amp;</span><span class="pln">counter</span><span class="pun">);</span><span class="pln">
        let handle </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"> </span><span class="pun">{</span><span class="pln">
            let mut num </span><span class="pun">=</span><span class="pln"> counter</span><span class="pun">.</span><span class="pln">lock</span><span class="pun">().</span><span class="pln">unwrap</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"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">});</span><span class="pln">
        handles</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">handle</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"> handle in handles </span><span class="pun">{</span><span class="pln">
        handle</span><span class="pun">.</span><span class="pln">join</span><span class="pun">().</span><span class="pln">unwrap</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">"Result: {}"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">counter</span><span class="pun">.</span><span class="pln">lock</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;">
	الشيفرة 14: محاولة استعمال <code>&lt;Rc&lt;T</code> للسماح لخيوط متعددة بامتلاك <code>&lt;Mutex&lt;T</code>
</p>

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

<pre class="ipsCode">$ cargo run
   Compiling shared-state v0.1.0 (file:///projects/shared-state)
error[E0277]: `Rc&lt;Mutex&lt;i32&gt;&gt;` cannot be sent between threads safely
  --&gt; src/main.rs:11:36
   |
11 |           let handle = thread::spawn(move || {
   |                        ------------- ^------
   |                        |             |
   |  ______________________|_____________within this `[closure@src/main.rs:11:36: 11:43]`
   | |                      |
   | |                      required by a bound introduced by this call
12 | |             let mut num = counter.lock().unwrap();
13 | |
14 | |             *num += 1;
15 | |         });
   | |_________^ `Rc&lt;Mutex&lt;i32&gt;&gt;` cannot be sent between threads safely
   |
   = help: within `[closure@src/main.rs:11:36: 11:43]`, the trait `Send` is not implemented for `Rc&lt;Mutex&lt;i32&gt;&gt;`
note: required because it's used within this closure
  --&gt; src/main.rs:11:36
   |
11 |         let handle = thread::spawn(move || {
   |                                    ^^^^^^^
note: required by a bound in `spawn`

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

<p>
	رسالة الخطأ هذه شديدة التعقيد، إليك الجزء المهم الذي يجب أن تركز عليه:
</p>

<pre class="ipsCode">`Rc&lt;Mutex&lt;i32&gt;&gt;` cannot be sent between threads safely
</pre>

<p>
	يخبرنا المصرّف أيضًا عن السبب:
</p>

<pre class="ipsCode">the trait `Send` is not implemented for `Rc&lt;Mutex&lt;i32&gt;&gt;`
</pre>

<p>
	سنتحدث عن <code>Send</code> في القسم التالي، إذ أنها أحد السمات التي تضمن أن الأنواع التي نستعملها مع الخيوط مخصصة للاستخدام في الحالات المتزامنة.
</p>

<p>
	لسوء الحظ فإن <code>&lt;Rc&lt;T</code> ليس آمنًا للمشاركة عبر الخيوط، فعندما يُدير <code>&lt;Rc&lt;T</code> عدد المراجع فإنه يضيف عدد كل استدعاء إلى <code>clone</code> ويطرح من العدد عندما تُحرَّر كل نسخة clone، إلا أنه لا يستعمل أي أنواع تزامن أولية للتأكد من أن التغييرات التي حدثت على العدد لا يمكن مقاطعتها بواسطة خيط آخر. قد يؤدي هذا إلى عمليات عدّ خاطئة -أخطاء خفية يمكن أن تؤدي بدورها إلى <a href="https://academy.hsoub.com/programming/rust/%D8%AD%D9%84%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-reference-cycles-%D9%88%D8%AA%D8%B3%D8%A8%D8%A8%D9%87%D8%A7-%D8%A8%D8%AA%D8%B3%D8%B1%D9%8A%D8%A8-%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-memory-leak-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2038/" rel="">تسريب الذاكرة memory leak</a> أو تحرير قيمة ما قبل أن ننتهي منها- وما نحتاجه هنا هو نوع مثل <code>&lt;Rc&lt;T</code> تمامًا ولكنه نوع يُجري تغييرات على عدد المراجع بطريقة آمنة للخيوط.
</p>

<h3>
	عد المراجع الذري باستخدام &lt;Arc&lt;T
</h3>

<p>
	لحسن الحظ، يعد <code>&lt;Arc&lt;T</code> نوعًا مثل <code>&lt;Rc&lt;T</code>وهو آمن للاستخدام في الحالات المتزامنة، إذ يرمز الحرف a إلى ذرّي atomic مما يعني أنه يُعد نوع عدّ مرجع ذري atomically reference counted type. تعدّ الأنواع الذرية Atomics نوعًا إضافيًا من أنواع التزامن الأولية concurrency primitive التي لن نغطيها بالتفصيل هنا. راجع <a href="https://doc.rust-lang.org/stable/std/sync/atomic/index.html" rel="external nofollow">توثيق المكتبة القياسية</a> للوحدة <a href="https://doc.rust-lang.org/stable/std/sync/atomic/index.html" rel="external nofollow"><code>std::sync::atomic</code></a> للمزيد من التفاصيل، من الكافي الآن معرفة أن الأنواع الذرية تعمل مثل الأنواع الأولية ولكن من الآمن مشاركتها عبر الخيوط.
</p>

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

<p>
	بالعودة إلى مثالنا: للنوعين <code>&lt;Arc&lt;T</code> و <code>&lt;Rc&lt;T</code> الواجهة البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> ذاتها لذلك نصحّح برنامجنا عن طريق تغيير سطر <code>use</code> واستدعاء <code>new</code> وكذلك استدعاء <code>clone</code>. نستطيع أخيرًا تصريف الشيفرة البرمجية في الشيفرة 15 وتنفيذها.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7084_14" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">sync</span><span class="pun">::{</span><span class="typ">Arc</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Mutex</span><span class="pun">};</span><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 counter </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Arc</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Mutex</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="lit">0</span><span class="pun">));</span><span class="pln">
    let mut handles </span><span class="pun">=</span><span class="pln"> vec</span><span class="pun">![];</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> _ in </span><span class="lit">0.</span><span class="pun">.</span><span class="lit">10</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let counter </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Arc</span><span class="pun">::</span><span class="pln">clone</span><span class="pun">(&amp;</span><span class="pln">counter</span><span class="pun">);</span><span class="pln">
        let handle </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"> </span><span class="pun">{</span><span class="pln">
            let mut num </span><span class="pun">=</span><span class="pln"> counter</span><span class="pun">.</span><span class="pln">lock</span><span class="pun">().</span><span class="pln">unwrap</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"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">});</span><span class="pln">
        handles</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">handle</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"> handle in handles </span><span class="pun">{</span><span class="pln">
        handle</span><span class="pun">.</span><span class="pln">join</span><span class="pun">().</span><span class="pln">unwrap</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">"Result: {}"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">counter</span><span class="pun">.</span><span class="pln">lock</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;">
	الشيفرة 15: استخدام <code>&lt;Arc&lt;T</code> لتغليف <code>&lt;Mutex&lt;T</code> بحيث نستطيع مشاركة الملكية عبر خيوط متعددة
</p>

<p>
	يكون خرج الشيفرة البرمجية السابقة على النحو التالي:
</p>

<pre class="ipsCode">Result: 10
</pre>

<p>
	وأخيرًا نجحنا، فقد عدَدنا من 0 إلى 10، وعلى الرغم من أن هذا قد لا يبدو رائعًا جدًا، إلا أنه علّمنا الكثير عن <code>&lt;Mutex&lt;T</code> وأمان الخيط. يمكنك أيضًا استخدام هيكل هذا البرنامج لإجراء عمليات أكثر تعقيدًا من مجرد زيادة العداد، ويمكننا باستخدام هذه الإستراتيجية تقسيم عملية حسابية إلى أجزاء مستقلة وتقسيم تلك الأجزاء عبر خيوط ثم استخدام <code>&lt;Mutex&lt;T</code> لجعل كل خيط يحدّث النتيجة النهائية بجزئه.
</p>

<p>
	لاحظ أنه إذا كنت تنفّذ عمليات عددية بسيطة فهناك أنواع أبسط من أنواع <code>&lt;Mutex&lt;T</code> التي توفرها <a href="https://doc.rust-lang.org/stable/std/sync/atomic/index.html" rel="external nofollow">وحدة المكتبة القياسية <code>std::sync::atomic</code></a>، إذ توفر هذه الأنواع وصولًا ذريًا آمنًا ومتزامنًا للأنواع الأولية، وقد اخترنا استخدام <code>&lt;Mutex&lt;T</code> مع نوع أولي لهذا المثال حتى نتمكن من التركيز على كيفية عمل <code>&lt;Mutex&lt;T</code>.
</p>

<h2>
	التشابه بين <code>&lt;RefCell&lt;T&gt;/Rc&lt;T</code> و <code>&lt;Mutex&lt;T&gt;/Arc&lt;T</code>
</h2>

<p>
	ربما لاحظت أن <code>counter</code> ثابت immutable ولكن يمكننا الحصول على مرجع متغيّر للقيمة الموجودة داخله، هذا يعني أن <code>&lt;Mutex&lt;T</code> يوفر قابلية تغيير داخلية كما تفعله عائلة <code>Cell</code>. نستخدم <code>&lt;Mutex&lt;T</code> بنفس الطريقة التي استخدمنا بها <code>&lt;RefCell&lt;T</code> سابقًا في مقال <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1-%D8%A7%D9%84%D8%B0%D9%83%D9%8A-refcell%E2%80%8E-%D9%88%D9%86%D9%85%D8%B7-%D9%82%D8%A7%D8%A8%D9%84%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%BA%D9%8A%D9%8A%D8%B1-%D8%A7%D9%84%D8%AF%D8%A7%D8%AE%D9%84%D9%8A-interior-mutability-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2037/" rel="">المؤشر الذكي Refcell<t>‎ ونمط قابلية التغيير الداخلي interior mutability في لغة رست Rust</t></a> للسماح لنا بتغيير المحتويات داخل <code>&lt;Rc&lt;T</code> وذلك لتغيير المحتويات داخل <code>Arc&lt;T&gt;‎</code>.
</p>

<p>
	هناك شيء آخر يجب ملاحظته ألا وهو أن رست لا يمكنها حمايتك من جميع أنواع الأخطاء المنطقية عند استخدام <code>&lt;Mutex&lt;T</code>. تذكر سابقًا في مقال <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1-rc%E2%80%8E-%D8%A7%D9%84%D8%B0%D9%83%D9%8A-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D9%87-%D9%84%D9%84%D8%A5%D8%B4%D8%A7%D8%B1%D8%A9-%D8%A5%D9%84%D9%89-%D8%B9%D8%AF%D8%AF-%D8%A7%D9%84%D9%85%D8%B1%D8%A7%D8%AC%D8%B9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2036/" rel="">المؤشر Rc<t>‎ الذكي واستخدامه للإشارة إلى عدد المراجع في لغة رست Rust</t></a> أن استخدام <code>&lt;Rc&lt;T</code> يأتي مع خطر إنشاء دورات مرجعية reference cycle، إذ تشير قيمتان <code>&lt;Rc&lt;T</code> إلى بعضهما بعضًا مما يتسبب في حدوث تسرب في الذاكرة. وبالمثل يأتي تطبيق <code>&lt;Mutex&lt;T</code> مع خطر خلق حالات مستعصية deadlocks، إذ تحدث هذه الحالات عندما تحتاج عملية ما إلى الحصول على مرجعين وحصل كل منهما على أحد الأقفال مما يتسبب في انتظارهما لبعضهما بعضًا إلى الأبد.
</p>

<p>
	إذا أثارت الحالات المستعصية اهتمامك وأردت رؤيتها عمليًا فحاول إنشاء برنامج رست به حالة مستعصية، ثم ابحث عن استراتيجيات التخفيف من الحالات المستعصية بالنسبة لكائنات المزامنة في أي لغة ونفذها في رست. يوفر توثيق واجهة المكتبة القياسية البرمجية لـ<code>&lt;Mutex&lt;T</code> و <code>MutexGuard</code> معلومات مفيدة.
</p>

<p>
	سنكمل هذا المقال بالحديث عن السمتين <code>Send</code> و <code>Sync</code> وكيف يمكننا استخدامها مع الأنواع المخصصة.
</p>

<h2>
	التزامن الموسع مع السمة Sync والسمة Send
</h2>

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

<p>
	ومع ذلك، ضُمِّنَ مفهومان للتزامن في اللغة: سمتَا <code>std::marker</code> وهما <code>Sync</code> و <code>Send</code>.
</p>

<h3>
	السماح بنقل الملكية بين الخيوط عن طريق Send
</h3>

<p>
	تشير الكلمة المفتاحية للسمة <code>Send</code> إلى أنه يمكن نقل ملكية القيم من النوع الذي ينفّذ <code>Send</code> بين الخيوط، ويطبّق كل نوع في رست تقريبًا السمة <code>Send</code>، ولكن هناك بعض الاستثناءات بما في ذلك <code>&lt;Rc&lt;T</code> إذ لا يمكن تطبيق السمة <code>Send</code> عليه لأنه إذا استنسخت قيمة <code>&lt;Rc&lt;T</code> وحاولت نقل ملكية الاستنساخ إلى خيط آخر فقد يحدّث كلا الخيطين عدد المراجع reference count في الوقت ذاته. لهذا السبب تُنفَّذ <code>&lt;Rc&lt;T</code> للاستخدام في المواقف أحادية الخيط حيث لا تريد دفع غرامة الأداء الآمن.
</p>

<p>
	لذلك يؤكد نظام نوع رست وحدود السمات trait bounds أنه لا يمكنك أبدًا إرسال قيمة <code>&lt;Rc&lt;T</code> بطريق الخطأ عبر الخيوط بصورةٍ غير آمنة. عندما حاولنا فعل ذلك في الشيفرة 14 سابقًا حصلنا على الخطأ:
</p>

<pre class="ipsCode">the trait Send is not implemented for Rc&lt;Mutex&lt;i32&gt;&gt;
</pre>

<p>
	وعندما استبدلنا النوع بالنوع <code>&lt;Arc&lt;T</code> الذي يطبّق السمة <code>Send</code> صُرِّفَت الشيفرة البرمجية بنجاح.
</p>

<p>
	أي نوع مكون بالكامل من أنواع <code>Send</code> يُميَّز تلقائيًا على أنه <code>Send</code> أيضًا، وتطبّق جميع الأنواع الأولية primitive types تقريبًا السمة <code>Send</code> بغض النظر عن المؤشرات الأولية primitive pointers التي سنناقشها لاحقًا في.
</p>

<h3>
	السماح بالوصول لخيوط متعددة باستخدام السمة Sync
</h3>

<p>
	تشير الكلمة المفتاحية للسمة <code>Sync</code> إلى أنه من الآمن للنوع المطبّق للسمة <code>Sync</code> أن يُشار إليه من خيوط متعددة، بمعنى آخر أي نوع <code>T</code> يطبّق السمة <code>Sync</code> إذا كان <code>T&amp;</code> (مرجع غير قابل للتغيير إلى <code>T</code>) يطبّق <code>Send</code> أيضًا، مما يعني أنه يمكن إرسال المرجع بأمان إلى خيط آخر، وبصورةٍ مشابهة للسمة <code>Send</code> فإن الأنواع الأولية تطبّق السمة <code>Sync</code> والأنواع المكونة كاملًا من أنواع تطبّق السمة <code>Sync</code> هي أيضًا أنواع تطبّق <code>Sync</code>.
</p>

<p>
	لا يطبّق المؤشر الذكي <code>&lt;Rc&lt;T</code> أيضًا السمة <code>Sync</code> للأسباب ذاتها التي تجعل منه غير قابل لتطبيق السمة <code>Send</code>، كما أن النمط <code>&lt;RefCell&lt;T</code> (الذي تحدثنا عنه <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1-%D8%A7%D9%84%D8%B0%D9%83%D9%8A-refcell%E2%80%8E-%D9%88%D9%86%D9%85%D8%B7-%D9%82%D8%A7%D8%A8%D9%84%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%BA%D9%8A%D9%8A%D8%B1-%D8%A7%D9%84%D8%AF%D8%A7%D8%AE%D9%84%D9%8A-interior-mutability-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2037/" rel="">سابقًا</a> وعائلة الأنواع المرتبطة بالنوع <code>&lt;Cell&lt;T</code> لا تطبّق السمة <code>Sync</code>. يعدّ تنفيذ فحص الاستعارة الذي تفعله <code>&lt;RefCell&lt;T</code> في وقت التنفيذ غير آمن للخيط. يطبّق المؤشر الذكي <code>&lt;Mutex&lt;T</code> السمة <code>Sync</code> ويمكن استخدامه لمشاركة الوصول مع خيوط متعددة كما رأيت سابقًا في قسم "مشاركة <code>&lt;Mutex&lt;T</code> بين خيوط متعددة".
</p>

<h3>
	تطبيق السمتين Send و Sync يدويا غير آمن
</h3>

<p>
	بما أن الأنواع المكونة من الأنواع التي تطبّق السمتين <code>Send</code> و<code>Sync</code> هي أنواع تطبّق السمتين <code>Send</code> و <code>Sync</code> تلقائيًا، فلا يتوجب علينا تطبيق هاتين السمتين يدويًا؛ وبصفتهما سمتان علّامة marker traits، فهما سمتان لا تحتويان أيّ توابع تطبّقها، وهما مفيدتان فقط لتعزيز الثوابت المرتبطة بالمزامنة.
</p>

<p>
	يشمل تنفيذ هاتان السمتان يدويًا تنفيذ شيفرة رست غير آمنة، وسنتحدث عن استعمال شيفرة رست غير آمنة لاحقًا، ومن الكافي الآن معرفتك أن بناء أنواع متزامنة جديدة غير مؤلفة من أجزاء <code>Send</code> و <code>Sync</code> يتطلّب تفكيرًا حذرًا للمحافظة على ضمانات الأمان في لغة رست. يحتوي الكتاب <a href="https://doc.rust-lang.org/stable/nomicon/index.html" rel="external nofollow">“The Rustonomicon”</a> معلومات أكثر عن هذه الضمانات وكيف يمكن التمسك بها.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html" rel="external nofollow">Fearless Concurrency</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%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-oop-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2097/" rel="">البرمجة كائنية التوجه <abbr title="Object-Oriented Programming | البرمجة كائنية التوجه">OOP</abbr> في لغة رست 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-%D9%85%D9%8A%D8%B2%D8%A9-%D8%AA%D9%85%D8%B1%D9%8A%D8%B1-%D8%A7%D9%84%D8%B1%D8%B3%D8%A7%D8%A6%D9%84-message-passing-%D9%84%D9%86%D9%82%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2045/" rel="">استخدام ميزة تمرير الرسائل Message Passing لنقل البيانات بين الخيوط Threads في لغة رست</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>
	<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%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%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/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%84%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%B1%D8%B3%D8%AA-%D8%A8%D8%B5%D9%88%D8%B1%D8%A9-%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D8%A2%D9%86%D9%8A%D9%8B%D8%A7-r2043/" rel="">استخدام الخيوط Threads لتنفيذ شيفرات رست بصورة متزامنة آنيًا</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2089</guid><pubDate>Mon, 21 Aug 2023 16:04:00 +0000</pubDate></item><item><title>&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x645;&#x64A;&#x632;&#x629; &#x62A;&#x645;&#x631;&#x64A;&#x631; &#x627;&#x644;&#x631;&#x633;&#x627;&#x626;&#x644; &#x644;&#x646;&#x642;&#x644; &#x627;&#x644;&#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; &#x628;&#x64A;&#x646; &#x627;&#x644;&#x62E;&#x64A;&#x648;&#x637; Threads &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A; Rust</title><link>https://academy.hsoub.com/programming/rust/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D9%8A%D8%B2%D8%A9-%D8%AA%D9%85%D8%B1%D9%8A%D8%B1-%D8%A7%D9%84%D8%B1%D8%B3%D8%A7%D8%A6%D9%84-%D9%84%D9%86%D9%82%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2045/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_08/---Message-Passing-----Threads----Rust.png.350572c08e42d1dbde2c92617c8f43e7.png" /></p>
<p>
	يُعد تمرير الرسائل message passing أحد الطرق الشائعة لضمان أمن التزامن، إذ تتواصل الخيوط threads أو المنفذون actors فيما بينهم بإرسال رسائل تحتوي على بيانات. يمكن توضيح هذه الفكرة باقتباس من شعار في <span ipsnoautolink="true">توثيق لغة البرمجة جو Go</span>: "لا تتواصل بمشاركة الذاكرة، شارك الذاكرة بالتواصل".
</p>

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

<p>
	تنقسم القناة إلى نصفين أحدها مُرسل transmitter والآخر مُستقبل receiver؛ والمُرسل هو القسم الموجود أعلى النهر الذي تضع فيه البطة المطاطية؛ والمُستقبل هو ما تصل إليه البطة المطاطية في نهاية النهر. يستدعي قسمٌ من الشيفرة البرمجية التوابع على المُرسل مع البيانات المراد إرسالها، والقسم الآخر يتحقق من وصول الرسالة في القسم المُستقبل. يمكن القول إن القناة قد أُغلقت إذا سقط أي من القسمين المُرسل أو المستقبل.
</p>

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

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3080_7" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">sync</span><span class="pun">::</span><span class="pln">mpsc</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="pun">(</span><span class="pln">tx</span><span class="pun">,</span><span class="pln"> rx</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> mpsc</span><span class="pun">::</span><span class="pln">channel</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 6: انشاء قناة وإسناد القسمين <code>tx</code> و <code>rx</code> إليها
</p>

<p>
	أنشأنا قناةً جديدةً باستخدام دالة <code>mpsc::channel</code>، ويعني الاختصار "mpsc" عدة منتجين multiple producer ومستهلك واحد single consumer. باختصار، طريقة تطبيق القنوات في مكتبة رست القياسية هي أن كل قناة تحتوي على عدة مُرسلين ينتجون القيم ولكن هناك مُستقبل واحد لاستهلاك تلك القيم. تخيّل عدة مجاري streams تلتقي في نهرٍ واحد كبير: أي سينتهي كل شيء يُرسل من المجاري في نهر واحد في النهاية. نبدأ بمنتج واحد وبعدها نضيف عدة منتجين بعد عمل هذا المثال.
</p>

<p>
	تُعيد الدالة <code>mpsc::channel</code> صفًا <span ipsnoautolink="true">tuple</span>، العنصر الأول هو طرف الإرسال (المُرسل) والعنصر الثاني هو طرف الاستقبال (المُستقبل)، وتُستخدم عادةً الاختصارات <code>tx</code> و <code>rx</code> في العديد من الحقول fields للمُرسل والمُستقبل على التوالي، لذا نُسمي متغيراتنا وفقًا لهذا الاصطلاح للدلالة على كل طرف.
</p>

<p>
	نستخدم التعليمة <code>let</code> بنمط pattern يدمّر هيكلية الصفوف، وسنتحدث عن استخدام الأنماط في تعليمة <code>let</code> وتدمير الهيكلية لاحقًا، ويكفي الآن معرفة أن استخدام تعليمة <code>let</code> هي الطريقة الأفضل لاستخراج أقسام من الصف المُعاد باستخدام <code>mpsc::channel</code>.
</p>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8393_10" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">sync</span><span class="pun">::</span><span class="pln">mpsc</span><span class="pun">;</span><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="pun">(</span><span class="pln">tx</span><span class="pun">,</span><span class="pln"> rx</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> mpsc</span><span class="pun">::</span><span class="pln">channel</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"> </span><span class="pun">{</span><span class="pln">
        let val </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">"hi"</span><span class="pun">);</span><span class="pln">
        tx</span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="pln">val</span><span class="pun">).</span><span class="pln">unwrap</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>tx</code> إلى خيط مُنشأ وإرسال "hi"
</p>

<p>
	استخدمنا <code>thread::spawn</code> مجددًا لإنشاء خيط جديد، ثم استخدمنا <code>move</code> لنقل <code>tx</code> إلى مُغلّف <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="">closure</a> بحيث يمتلك الخيط المُنشأ القيمة <code>tx</code>. يجب على الخيط المُنشأ أن يمتلك الطرف المُرسل لكي يستطيع إرسال رسائل عبر القناة، ولدى المرسل تابع <code>send</code> الذي يأخذ القيمة المُراد إرسالها، إذ يعيد التابع <code>send</code> قيمةً من النوع <code>Result&lt;T, E&gt;‎</code>، لذا إذا كان المُستقبل قد اُسقط وليس هناك مكان لإرسال القيمة، تعيد عملية الإرسال خطأً. استدعينا في هذا المثال <code>unwrap</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>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8393_12" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">sync</span><span class="pun">::</span><span class="pln">mpsc</span><span class="pun">;</span><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="pun">(</span><span class="pln">tx</span><span class="pun">,</span><span class="pln"> rx</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> mpsc</span><span class="pun">::</span><span class="pln">channel</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"> </span><span class="pun">{</span><span class="pln">
        let val </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">"hi"</span><span class="pun">);</span><span class="pln">
        tx</span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="pln">val</span><span class="pun">).</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    let received </span><span class="pun">=</span><span class="pln"> rx</span><span class="pun">.</span><span class="pln">recv</span><span class="pun">().</span><span class="pln">unwrap</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"> received</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 8: استقبال القيمة "hi" في الخيط الأساسي وطباعتها
</p>

<p>
	للمستقبل تابعان مفيدان، هما <code>recv</code> و <code>try_recv</code>، استخدمنا <code>recv</code> -وهو اختصارٌ لكلمة استقبال receive- الذي يمنع تنفيذ الخيط الرئيسي وينتظر حتى تصل القيمة إلى نهاية القناة، ويعيد التابع <code>recv</code> بعد وصول القيمة إلى نهاية القناة القيمة ذاتها داخل النوع <code>Result&lt;T,E&gt;‎</code>، وعندما يُغلق المُرسل يعيد التابع <code>recv</code> خطأ للإشارة إلى أنه لا يوجد المزيد من القيم قادمة.
</p>

<p>
	لا يحجب التابع <code>try_recv</code> الخيط الرئيسي وإنما يعيد قيمةً من النوع <code>Result&lt;T,E&gt;‎</code> مباشرةً، وتحتوي قيمة <code>Ok</code> رسالةً إذا كان هناك رسالة متوفرة وإلا فقيمة <code>Err</code> إذا لا يوجد أي رسائل هذه المرة. استخدام <code>try_recv</code> مفيدٌ إذا كان للخيط أعمالٌ أخرى لينفذها بينما ينتظر الرسائل، وبإمكاننا كتابة حلقة تستدعي <code>try_recv</code> بصورةٍ متكررة لتتعامل مع الرسائل في حال قدومها، أو تنفّذ أعمالًا أخرى قبل تحققها مجدداً.
</p>

<p>
	استخدمنا في مثالنا التابع <code>recv</code> لبساطته، وليس لدينا أي عمل آخر للخيط الرئيسي غير انتظار الرسائل، لذا فإن حجب الخيط الرئيس مناسب.
</p>

<p>
	عندما ننفذ الشيفرة البرمجية في الشيفرة 8 نلاحظ القيمة المطبوعة في الخيط الرئيسي:
</p>

<pre class="ipsCode">Got: hi
</pre>

<p>
	هذا ممتاز!
</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> دورًا مهمًا في إرسال الرسائل لأنها تساعد في كتابة شيفرة آمنة ومتزامنة، إذ يتطلب منع الأخطاء في البرمجة المتزامنة الانتباه على الملكية ضمن برنامجك باستخدام لغة رست. لنكتب مثالًا يظهر كيف تعمل كل من القنوات والملكية لمنع المشاكل، وسنستخدم قيمة <code>val</code> في الخيط المُنشأ بعد أن أرسلناه عبر القناة. جرّب تصريف الشيفرة البرمجية في الشيفرة 9 لترى سبب كون هذه الشيفرة غير مسموحة.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8393_14" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">sync</span><span class="pun">::</span><span class="pln">mpsc</span><span class="pun">;</span><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="pun">(</span><span class="pln">tx</span><span class="pun">,</span><span class="pln"> rx</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> mpsc</span><span class="pun">::</span><span class="pln">channel</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"> </span><span class="pun">{</span><span class="pln">
        let val </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">"hi"</span><span class="pun">);</span><span class="pln">
        tx</span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="pln">val</span><span class="pun">).</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"val is {}"</span><span class="pun">,</span><span class="pln"> val</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    let received </span><span class="pun">=</span><span class="pln"> rx</span><span class="pun">.</span><span class="pln">recv</span><span class="pun">().</span><span class="pln">unwrap</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"> received</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

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

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

<pre class="ipsCode">$ cargo run
   Compiling message-passing v0.1.0 (file:///projects/message-passing)
error[E0382]: borrow of moved value: `val`
  --&gt; src/main.rs:10:31
   |
8  |         let val = String::from("hi");
   |             --- move occurs because `val` has type `String`, which does not implement the `Copy` trait
9  |         tx.send(val).unwrap();
   |                 --- value moved here
10 |         println!("val is {}", val);
   |                               ^^^ value borrowed here after move
   |
   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

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

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

<h2>
	إرسال قيم متعددة مع انتظار المستقبِل
</h2>

<p>
	استطعنا تصريف وتنفيذ الشيفرة 8، ولكن لم يظهر لنا أن خيطين مستقلين كانا يتكلمان مع بعضهما عبر القناة. أجرينا بعض التعديلات في الشيفرة 10 لنبين أن الشيفرة البرمجية في الشيفرة 8 تُنفَّذ بصورةٍ متزامنة. سيُرسل الخيط المُنشأ الآن عدة رسائل وسيتوقف لمدة ثانية بين كل رسالة.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8393_17" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">sync</span><span class="pun">::</span><span class="pln">mpsc</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">thread</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">time</span><span class="pun">::</span><span class="typ">Duration</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="pun">(</span><span class="pln">tx</span><span class="pun">,</span><span class="pln"> rx</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> mpsc</span><span class="pun">::</span><span class="pln">channel</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"> </span><span class="pun">{</span><span class="pln">
        let vals </span><span class="pun">=</span><span class="pln"> vec</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">"hi"</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">"from"</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">"the"</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">"thread"</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"> val in vals </span><span class="pun">{</span><span class="pln">
            tx</span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="pln">val</span><span class="pun">).</span><span class="pln">unwrap</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">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">for</span><span class="pln"> received in rx </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"> received</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: إرسال رسائل متعددة مع التوقف بين كل عملية إرسال
</p>

<p>
	للخيط المُنشأ هذه المرة شعاع <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> من السلاسل النصية التي نريد إرسالها إلى الخيط الأساسي. نمرّ على السلاسل النصية ونرسلها بصورةٍ إفرادية ونتوقف بين كل واحدة وأخرى باستدعاء دالة <code>thread::sleep</code> مع قيمة <code>Duration</code> مساوية إلى 1 ثانية.
</p>

<p>
	لم نستدعِ في الخيط الأساسي الدالة <code>recv</code> صراحةً بل عاملنا <code>rx</code> مثل مكرّر <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="">iterator</a>، إذ نطبع كل قيمة نستقبلها، وتنتهي عملية التكرار عندما تُغلق القناة.
</p>

<p>
	نلاحظ الخرج التالية عند تنفيذ الشيفرة البرمجية في الشيفرة 10 مع توقف لمدة ثانية بين كل سطر وآخر:
</p>

<pre class="ipsCode">Got: hi
Got: from
Got: the
Got: thread
</pre>

<p>
	يمكننا معرفة أن الخيط الأساسي ينتظر استلام القيم من الخيط المُنشأ لأنه ليس لدينا شيفرة تتوقف أو تتأخر في الحلقة <code>for</code> ضمن الخيط الأساسي.
</p>

<h2>
	إنشاء عدة منتجين بواسطة نسخ المرسل
</h2>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8393_19" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">sync</span><span class="pun">::</span><span class="pln">mpsc</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">thread</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">time</span><span class="pun">::</span><span class="typ">Duration</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">

    let </span><span class="pun">(</span><span class="pln">tx</span><span class="pun">,</span><span class="pln"> rx</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> mpsc</span><span class="pun">::</span><span class="pln">channel</span><span class="pun">();</span><span class="pln">

    let tx1 </span><span class="pun">=</span><span class="pln"> tx</span><span class="pun">.</span><span class="pln">clone</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"> </span><span class="pun">{</span><span class="pln">
        let vals </span><span class="pun">=</span><span class="pln"> vec</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">"hi"</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">"from"</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">"the"</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">"thread"</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"> val in vals </span><span class="pun">{</span><span class="pln">
            tx1</span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="pln">val</span><span class="pun">).</span><span class="pln">unwrap</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">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">

    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"> </span><span class="pun">{</span><span class="pln">
        let vals </span><span class="pun">=</span><span class="pln"> vec</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">"more"</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">"messages"</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">"for"</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">"you"</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"> val in vals </span><span class="pun">{</span><span class="pln">
            tx</span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="pln">val</span><span class="pun">).</span><span class="pln">unwrap</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">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">for</span><span class="pln"> received in rx </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"> received</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;">
	الشيفرة 11: إرسال عدّة رسائل من عدّة منتجين
</p>

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

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

<pre class="ipsCode">Got: hi
Got: more
Got: from
Got: messages
Got: for
Got: the
Got: thread
Got: you
</pre>

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

<p>
	الآن وبعد تعلمنا كيفية عمل القنوات سنتعلم في المقالات التالية نوعًا آخرًا من التزامن.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html" rel="external nofollow">Fearless Concurrency</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%B2%D8%A7%D9%85%D9%86-%D8%A7%D9%84%D8%AD%D8%A7%D9%84%D8%A9-%D8%A7%D9%84%D9%85%D8%B4%D8%AA%D8%B1%D9%83%D8%A9-shared-state-concurrency-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-%D9%88%D8%AA%D9%88%D8%B3%D9%8A%D8%B9-%D8%A7%D9%84%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D9%85%D8%B9-send-%D9%88-sync-r2089/" rel="">تزامن الحالة المشتركة Shared-State Concurrency في لغة رست وتوسيع التزامن مع Send و Sync</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%AE%D9%8A%D9%88%D8%B7-threads-%D9%84%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%B1%D8%B3%D8%AA-%D8%A8%D8%B5%D9%88%D8%B1%D8%A9-%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D8%A2%D9%86%D9%8A%D9%8B%D8%A7-r2043/" rel="">استخدام الخيوط Threads لتنفيذ شيفرات رست بصورة متزامنة آنيًا</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%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/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">2045</guid><pubDate>Mon, 14 Aug 2023 16:02:00 +0000</pubDate></item><item><title>&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x627;&#x644;&#x62E;&#x64A;&#x648;&#x637; Threads &#x644;&#x62A;&#x646;&#x641;&#x64A;&#x630; &#x634;&#x64A;&#x641;&#x631;&#x627;&#x62A; &#x631;&#x633;&#x62A; &#x628;&#x635;&#x648;&#x631;&#x629; &#x645;&#x62A;&#x632;&#x627;&#x645;&#x646;&#x629; &#x622;&#x646;&#x64A;&#x64B;&#x627;</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%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%84%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%B1%D8%B3%D8%AA-%D8%A8%D8%B5%D9%88%D8%B1%D8%A9-%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D8%A2%D9%86%D9%8A%D9%8B%D8%A7-r2043/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_08/--Threads----Rust--.png.4bbcb1c606ed965a0dfd3bcb6b26ef2b.png" /></p>
<p>
	تُنفّذ شيفرة البرنامج في معظم أنظمة التشغيل الحالية ضمن عملية process ويُدير نظام التشغيل عمليات متعددة في وقت واحد، يمكنك أيضًا العثور على أجزاء مستقلة تعمل بصورةٍ متزامنة داخل البرنامج، وتسمى الميّزات التي تنفّذ هذه الأجزاء المستقلة بالخيوط threads، على سبيل المثال يمكن أن يحتوي خادم الويب web server على خيوط متعددة بحيث يمكنه أن يستجيب لأكثر من طلب واحد في نفس الوقت.
</p>

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

<ul>
	<li>
		حالات التسابق race conditions، إذ يمكن للخيوط الوصول للبيانات أو المصادر بترتيب غير مضبوط.
	</li>
	<li>
		أقفال ميتة deadlocks، وهي الحالة التي ينتظر فيها الخيطان بعضهما بعضًا مما يمنع كلا الخيطين من الاستمرار.
	</li>
	<li>
		الأخطاء التي تحدث فقط في حالات معينة وتكون صعبة الحدوث دومًا والإصلاح بصورةٍ موثوقة.
	</li>
</ul>

<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>
	تُنفّذ لغات البرمجة الخيوط بطرق مختلفة عن بعضها البعض، وتوفر العديد من <a href="https://academy.hsoub.com/apps/operating-systems/%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84/" rel="">أنظمة التشغيل</a> واجهة برمجية يمكن للغة أن تستدعيها لإنشاء خيوط جديدة. تستخدم مكتبة رست القياسية نموذج 1:1 لتنفيذ الخيط، إذ يستخدم البرنامج خيط نظام تشغيل واحد لكل خيط لغة. هناك وحدات مصرفة تنفذ نماذج أخرى للخيوط لتقديم مقايضات مختلفة عن نموذج 1:1.
</p>

<h2>
	إنشاء خيط جديد باستخدام spawn
</h2>

<p>
	نستدعي الدالة <code>thread::spawn</code> لإنشاء خيط جديد ونمرر لها مغلَّف closure يحتوي على الشيفرة التي نريد أن ننفذها في الخيط الجديد (تحدثنا عن المغلفات سابقًا في المقال <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 في لغة رست</a>). يطبع المثال في الشيفرة 1 نصًا ما من خيط رئيسي ونص آخر من خيط جديد:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3389_9" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">thread</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">time</span><span class="pun">::</span><span class="typ">Duration</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">
    thread</span><span class="pun">::</span><span class="pln">spawn</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"> i in </span><span class="lit">1.</span><span class="pun">.</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">"hi number {} from the spawned thread!"</span><span class="pun">,</span><span class="pln"> i</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_millis</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><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> i in </span><span class="lit">1.</span><span class="pun">.</span><span class="lit">5</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"hi number {} from the main thread!"</span><span class="pun">,</span><span class="pln"> i</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_millis</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;">
	الشيفرة 1: إنشاء خيط جديد لطباعة شيء ما بينما يطبع الخيط الرئيسي شيئًا آخر
</p>

<p>
	لاحظ أنه عندما يكتمل الخيط الرئيسي لبرنامج رست، تُغلَق كل الخيوط المُنشأة بغض النظر إذا أنهت التنفيذ أم لا، ويمكن لخرج هذا البرنامج أن يكون مختلفًا في كل مرة ولكنه سيكون مشابهًا لما يلي:
</p>

<pre class="ipsCode">hi number 1 from the main thread!
hi number 1 from the spawned thread!
hi number 2 from the main thread!
hi number 2 from the spawned thread!
hi number 3 from the main thread!
hi number 3 from the spawned thread!
hi number 4 from the main thread!
hi number 4 from the spawned thread!
hi number 5 from the spawned thread!
</pre>

<p>
	تؤدي استدعاءات <code>thread::sleep</code> إلى إجبار الخيط على إيقاف تنفيذه لمدة قصيرة مما يسمح بتشغيل خيوط مختلفة، ومن المحتمل أن تتناوب الخيوط ولكن هذا الأمر غير مضمون الحدوث، إذ يعتمد ذلك على كيفية جدولة نظام التشغيل الخاص للخيوط. يُطبع في التنفيذ السابق الخيط الرئيسي أولًا على الرغم من ظهور عبارة الطباعة من الخيط الذي أُنشئ أولًا في الشيفرة البرمجية، وعلى الرغم من أننا أخبرنا الخيط المُنشأ أن يطبع حتى تصبح <code>i</code> مساوية للقيمة 9 إلا أنه وصل إلى 5 فقط قبل إغلاق الخيط الرئيسي.
</p>

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

<h2>
	انتظار انتهاء كل الخيوط بضم المقابض Handles عن طريق join
</h2>

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

<p>
	يمكننا إصلاح مشكلة عدم تشغيل الخيط الناتج spawned أو انتهائه قبل الأوان عن طريق حفظ قيمة إرجاع <code>thread::spawn</code> في متغير، إذ يكون النوع المُعاد من <code>thread::spawn</code> هو <code>JoinHandle</code>؛ الذي يمثّل قيمةً مملوكةً، بحيث عندما نستدعي التابع <code>join</code> عليها ستنتظر حتى ينتهي الخيط الخاص بها. تُظهر الشيفرة 2 كيفية استعمال <code>JoinHandle</code> الخاص بالخيط التي أنشأناه في الشيفرة 1 واستدعاء <code>join</code> للتأكد من انتهاء الخيط المنتج قبل الخروج من الدالة <code>main</code>:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3389_11" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">thread</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">time</span><span class="pun">::</span><span class="typ">Duration</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 handle </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"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> i in </span><span class="lit">1.</span><span class="pun">.</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">"hi number {} from the spawned thread!"</span><span class="pun">,</span><span class="pln"> i</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_millis</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><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> i in </span><span class="lit">1.</span><span class="pun">.</span><span class="lit">5</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"hi number {} from the main thread!"</span><span class="pun">,</span><span class="pln"> i</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_millis</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">

    handle</span><span class="pun">.</span><span class="pln">join</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;">
	الشيفرة 2: حفظ <code>JoinHandle</code> من <code>thread::spawn</code> لضمان تنفيذ الخيط لحين الاكتمال
</p>

<p>
	يؤدي استدعاء <code>join</code> على المقبض handle إلى إيقاف تنفيذ الخيط الجاري حتى ينتهي الخيط الذي يمثله المقبض، ويعني إيقاف تنفيذ blocking الخيط أن الخيط ممنوع من أداء العمل أو الخروج منه. يجب أن ينتج تنفيذ الشيفرة 2 خرجًا مشابهًا لما يلي لأننا وضعنا استدعاء <code>join</code> بعد حلقة الخيط الرئيسي <code>for</code>:
</p>

<pre class="ipsCode">hi number 1 from the main thread!
hi number 2 from the main thread!
hi number 1 from the spawned thread!
hi number 3 from the main thread!
hi number 2 from the spawned thread!
hi number 4 from the main thread!
hi number 3 from the spawned thread!
hi number 4 from the spawned thread!
hi number 5 from the spawned thread!
hi number 6 from the spawned thread!
hi number 7 from the spawned thread!
hi number 8 from the spawned thread!
hi number 9 from the spawned thread!
</pre>

<p>
	يستمرّ الخيطان بالتناوب، وينتظر الخيط الرئيسي بسبب استدعاء <code>()handle.join</code>، ولا ينتهي حتى ينتهي الخيط الناتج.
</p>

<p>
	دعنا نرى ما سيحدث عندما ننقل <code>()handle.join</code> إلى ما قبل حلقة <code>for</code> في <code>main</code> على النحو التالي:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3389_13" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">thread</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">time</span><span class="pun">::</span><span class="typ">Duration</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 handle </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"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> i in </span><span class="lit">1.</span><span class="pun">.</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">"hi number {} from the spawned thread!"</span><span class="pun">,</span><span class="pln"> i</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_millis</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><span class="pln">

    handle</span><span class="pun">.</span><span class="pln">join</span><span class="pun">().</span><span class="pln">unwrap</span><span class="pun">();</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> i in </span><span class="lit">1.</span><span class="pun">.</span><span class="lit">5</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"hi number {} from the main thread!"</span><span class="pun">,</span><span class="pln"> i</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_millis</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>for</code> الخاصة به لذلك لن تتداخل المخرجات بعد الآن كما هو موضح هنا:
</p>

<pre class="ipsCode">hi number 1 from the spawned thread!
hi number 2 from the spawned thread!
hi number 3 from the spawned thread!
hi number 4 from the spawned thread!
hi number 5 from the spawned thread!
hi number 6 from the spawned thread!
hi number 7 from the spawned thread!
hi number 8 from the spawned thread!
hi number 9 from the spawned thread!
hi number 1 from the main thread!
hi number 2 from the main thread!
hi number 3 from the main thread!
hi number 4 from the main thread!
</pre>

<p>
	يمكن أن تؤثر التفاصيل الصغيرة على ما إذا كانت الخيوط الخاصة بك تُنفَّذ في نفس الوقت أم لا مثل استدعاء <code>join</code>.
</p>

<h2>
	استعمال مغلفات move مع الخيوط
</h2>

<p>
	نستخدم غالبًا الكلمة المفتاحية <code>move</code> مع المغلفات التي تُمرَّر إلى <code>thread::spawn</code> وذلك لأن المغلف سيأخذ بعد ذلك ملكية القيم التي يستخدمها من البيئة وبالتالي تُنقل ملكية هذه القيم من خيط إلى آخر، ناقشنا سابقًا في فقرة "الحصول على المعلومات من البيئة باستخدام المغلفات" من مقال <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 في لغة رست</a> الكلمة المفتاحية <code>move</code> في سياق المغلفات، إلا أننا سنركز الآن أكثر على التفاعل بين <code>move</code> و <code>thread::spawn</code>.
</p>

<p>
	لاحظ في الشيفرة 1 أن المغلف الذي نمرّره إلى <code>thread::spawn</code> لا يأخذ أي وسطاء arguments، إذ أننا لا نستخدم أي بيانات من الخيط الرئيسي في شيفرة الخيط المنتج، ولاستخدام البيانات من الخيط الرئيسي في الخيط المُنتج يجب أن يحصل مغلّف الخيط الناتج على القيم التي يحتاجها. تُظهر الشيفرة 3 محاولة إنشاء شعاع vector في الخيط الرئيسي واستعماله في الخيط الناتج، ومع ذلك لن ينجح هذا الأمر كما سترى بعد لحظة.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3389_17" 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 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">

    let handle </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"> </span><span class="pun">{</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"Here's a vector: {:?}"</span><span class="pun">,</span><span class="pln"> v</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    handle</span><span class="pun">.</span><span class="pln">join</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;">
	الشيفرة 3: محاولة استعمال شعاع منشأ بواسطة الخيط الرئيسي ضمن خيط آخر
</p>

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

<pre class="ipsCode">$ cargo run
   Compiling threads v0.1.0 (file:///projects/threads)
error[E0373]: closure may outlive the current function, but it borrows `v`, which is owned by the current function
 --&gt; src/main.rs:6:32
  |
6 |     let handle = thread::spawn(|| {
  |                                ^^ may outlive borrowed value `v`
7 |         println!("Here's a vector: {:?}", v);
  |                                           - `v` is borrowed here
  |
note: function requires argument type to outlive `'static`
 --&gt; src/main.rs:6:18
  |
6 |       let handle = thread::spawn(|| {
  |  __________________^
7 | |         println!("Here's a vector: {:?}", v);
8 | |     });
  | |______^
help: to force the closure to take ownership of `v` (and any other referenced variables), use the `move` keyword
  |
6 |     let handle = thread::spawn(move || {
  |                                ++++

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

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

<p>
	تقدّم الشيفرة 4 سيناريو من المرجح به أن يحتوي على مرجع غير صالح إلى <code>v</code>:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3389_19" 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 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">

    let handle </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"> </span><span class="pun">{</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"Here's a vector: {:?}"</span><span class="pun">,</span><span class="pln"> v</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    drop</span><span class="pun">(</span><span class="pln">v</span><span class="pun">);</span><span class="pln"> </span><span class="com">// oh no!</span><span class="pln">

    handle</span><span class="pun">.</span><span class="pln">join</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;">
	الشيفرة 4: خيط مع مغلف يحاول أن يحصل على مرجع يشير للشعاع <code>v</code> من خيط رئيسي يحرّر <code>v</code>
</p>

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

<p>
	لتصحيح الخطأ التصريفي في الشيفرة 3 نتّبع النصيحة المزودة لنا في رسالة الخطأ:
</p>

<pre class="ipsCode">help: to force the closure to take ownership of `v` (and any other referenced variables), use the `move` keyword
  |
6 |     let handle = thread::spawn(move || {
  |                                ++++
</pre>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3389_23" 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 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">

    let handle </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"> </span><span class="pun">{</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"Here's a vector: {:?}"</span><span class="pun">,</span><span class="pln"> v</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    handle</span><span class="pun">.</span><span class="pln">join</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;">
	الشيفرة 5: استعمال الكلمة المفتاحية <code>move</code> لنفرض على المغلف أن يأخذ ملكية القيم التي يستعملها
</p>

<p>
	قد نرغب في تجربة الأمر ذاته لتصحيح الشيفرة البرمجية في الشيفرة 4، إذ استدعى الخيط الرئيسي <code>drop</code> باستعمال مغلف <code>move</code>، ومع ذلك فإن هذا لن يعمل لأن ما تحاول الشيفرة 4 فعله غير مسموح به لسبب مختلف؛ وإذا أضفنا <code>move</code> إلى المغلف فسننقل <code>v</code> إلى بيئة المغلف ولن يعد بإمكاننا بعد ذلك استدعاء <code>drop</code> عليه في الخيط الرئيسي، وسنحصل على الخطأ التصريفي التالي بدلًا من ذلك:
</p>

<pre class="ipsCode">$ cargo run
   Compiling threads v0.1.0 (file:///projects/threads)
error[E0382]: use of moved value: `v`
  --&gt; src/main.rs:10:10
   |
4  |     let v = vec![1, 2, 3];
   |         - move occurs because `v` has type `Vec&lt;i32&gt;`, which does not implement the `Copy` trait
5  |
6  |     let handle = thread::spawn(move || {
   |                                ------- value moved into closure here
7  |         println!("Here's a vector: {:?}", v);
   |                                           - variable moved due to use in closure
...
10 |     drop(v); // oh no!
   |          ^ value used here after move

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

<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> مرةً أخرى، إذ حصلنا على خطأ في الشيفرة 3 لأن رست كانت صارمة باستعارة <code>v</code> للخيط ذاته فقط مما يعني أن الخيط الرئيسي يمكنه نظريًا إبطال مرجع الخيط الناتج، ويمكننا بإخبار رست أن تنقل ملكية <code>v</code> إلى الخيط الناتج أن نضمن لرست أن الخيط الرئيسي لن يستخدم <code>v</code> بعد الآن. إذا عدّلنا الشيفرة 4 بالطريقة ذاتها فإننا بذلك ننتهك قواعد الملكية عندما نحاول استعمال <code>v</code> في الخيط الرئيسي. تتجاوز الكلمة المفتاحية <code>move</code> الوضع الافتراضي الصارم للاستعارة في رست، فلا يُسمح لنا بانتهاك قواعد الملكية.
</p>

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

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html" rel="external nofollow">Fearless Concurrency</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-%D9%85%D9%8A%D8%B2%D8%A9-%D8%AA%D9%85%D8%B1%D9%8A%D8%B1-%D8%A7%D9%84%D8%B1%D8%B3%D8%A7%D8%A6%D9%84-message-passing-%D9%84%D9%86%D9%82%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2045/" rel="">استخدام ميزة تمرير الرسائل لنقل البيانات بين الخيوط Threads في لغة رست Rust </a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%AD%D9%84%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-reference-cycles-%D9%88%D8%AA%D8%B3%D8%A8%D8%A8%D9%87%D8%A7-%D8%A8%D8%AA%D8%B3%D8%B1%D9%8A%D8%A8-%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-memory-leak-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2038/" rel="">حلقات المرجع Reference Cycles وتسببها بتسريب الذاكرة Memory Leak في لغة رست 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%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/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="">مقدمة إلى الخيوط Threads في جافا</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2043</guid><pubDate>Tue, 08 Aug 2023 16:00:00 +0000</pubDate></item><item><title>&#x62D;&#x644;&#x642;&#x627;&#x62A; &#x627;&#x644;&#x645;&#x631;&#x62C;&#x639; Reference Cycles &#x648;&#x62A;&#x633;&#x628;&#x628;&#x647;&#x627; &#x628;&#x62A;&#x633;&#x631;&#x64A;&#x628; &#x627;&#x644;&#x630;&#x627;&#x643;&#x631;&#x629; Memory Leak &#x641;&#x64A; &#x644;&#x63A;&#x629; Rust</title><link>https://academy.hsoub.com/programming/rust/%D8%AD%D9%84%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-reference-cycles-%D9%88%D8%AA%D8%B3%D8%A8%D8%A8%D9%87%D8%A7-%D8%A8%D8%AA%D8%B3%D8%B1%D9%8A%D8%A8-%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-memory-leak-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-rust-r2038/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_08/--Reference-Cycles----Memory-Leak----Rust.png.d389b374e59f1189ccc2abc43df25240.png" /></p>
<p>
	قد تكون عملية ملء الذاكرة دون تحريرها (العملية المعروفة بتسريب الذاكرة memory leak) صعبة الحدوث بمستوى أمان الذاكرة الذي تقدمه لغة رست Rust، إلا أن حدوث هذا الأمر غير مستحيل، إذ لا تضمن رست منع تسريب الذاكرة بصورةٍ كاملة، والمقصود هنا أن الذاكرة المُسرّبة آمنة في رست. نلاحظ أن رست تسمح بتسريب الذاكرة باستخدام <code>Rc&lt;T&gt;‎</code> و <code>RefCell&lt;T&gt;‎</code>، إذ أنه من الممكن إنشاء مراجع تشير إلى بعضها ضمن حلقة cycle، وسيسبب هذا تسريب ذاكرة لأن عدد المراجع لكل عنصر لن يصل إلى 0 أبدًا، وبهذا لن تُحرَّر أي قيمة.
</p>

<h2>
	إنشاء حلقة مرجع
</h2>

<p>
	لنلاحظ كيف من الممكن أن نشكّل حلقة مرجع لتفادي هذا الأمر، بدءًا بتعريف معدّد <code>List</code> وتابع <code>tail</code> في الشيفرة 25:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6566_9" 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">
use std</span><span class="pun">::</span><span class="pln">cell</span><span class="pun">::</span><span class="typ">RefCell</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">rc</span><span class="pun">::</span><span class="typ">Rc</span><span class="pun">;</span><span class="pln">

</span><span class="com">#[derive(Debug)]</span><span class="pln">
</span><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">RefCell</span><span class="pun">&lt;</span><span class="typ">Rc</span><span class="pun">&lt;</span><span class="typ">List</span><span class="pun">&gt;&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">

impl </span><span class="typ">List</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn tail</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">Option</span><span class="pun">&lt;&amp;</span><span class="typ">RefCell</span><span class="pun">&lt;</span><span class="typ">Rc</span><span class="pun">&lt;</span><span class="typ">List</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        match self </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">Cons</span><span class="pun">(</span><span class="pln">_</span><span class="pun">,</span><span class="pln"> item</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">item</span><span class="pun">),</span><span class="pln">
            </span><span class="typ">Nil</span><span class="pln"> </span><span class="pun">=&gt;</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="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 style="text-align: center;">
	الشيفرة 25: تعريف قائمة بنية تخزّن <code>RefCell&lt;T&gt;‎</code> داخلها، بحيث نستطيع تعديل القيمة التي يشير إليها متغاير <code>Cons</code>
</p>

<p>
	نستخدم هنا نوعًا مختلفًا من تعريف <code>List</code> من الشيفرة 5، إذ يصبح العنصر الثاني من المتغاير <code>Cons</code> أي variant مساويًا للنوع <code>RefCell&lt;Rc&lt;List&gt;&gt;‎</code>، مما يعني أننا نحتاج تعديل قيمة <code>List</code> المشار إليها في <code>Cons</code> عوضًا عن حاجتنا لقابلية التعديل على قيمة النوع <code>i32</code> كما فعلنا في الشيفرة 24 في <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1-%D8%A7%D9%84%D8%B0%D9%83%D9%8A-refcell%E2%80%8E-%D9%88%D9%86%D9%85%D8%B7-%D9%82%D8%A7%D8%A8%D9%84%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%BA%D9%8A%D9%8A%D8%B1-%D8%A7%D9%84%D8%AF%D8%A7%D8%AE%D9%84%D9%8A-interior-mutability-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2037/" rel="">المقال السابق</a>، نضيف أيضًا التابع <code>tail</code> لتسهيل الوصول إلى العنصر الثاني إذا وُجد متغاير <code>Cons</code>.
</p>

<p>
	أضفنا في الشيفرة 26 الدالة <code>main</code> التي تستخدم التعريف الموجود في الشيفرة 25، إذ تُنشئ الشيفرة قائمةً في <code>a</code> وقائمة في <code>b</code> تشير إلى القائمة <code>a</code>، وثم تعدِّل القائمة في <code>a</code> لتشير إلى <code>b</code> لتنشئ بذلك حلقة مرجع. كتبنا أيضًا تعليمات <code>println!‎</code> لتظهر قيمة عدد المراجع في نقاط مختلفة ضمن هذه العملية.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6566_11" 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">
use std</span><span class="pun">::</span><span class="pln">cell</span><span class="pun">::</span><span class="typ">RefCell</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">rc</span><span class="pun">::</span><span class="typ">Rc</span><span class="pun">;</span><span class="pln">

</span><span class="com">#[derive(Debug)]</span><span class="pln">
</span><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">RefCell</span><span class="pun">&lt;</span><span class="typ">Rc</span><span class="pun">&lt;</span><span class="typ">List</span><span class="pun">&gt;&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">

impl </span><span class="typ">List</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn tail</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">Option</span><span class="pun">&lt;&amp;</span><span class="typ">RefCell</span><span class="pun">&lt;</span><span class="typ">Rc</span><span class="pun">&lt;</span><span class="typ">List</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        match self </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">Cons</span><span class="pun">(</span><span class="pln">_</span><span class="pun">,</span><span class="pln"> item</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">item</span><span class="pun">),</span><span class="pln">
            </span><span class="typ">Nil</span><span class="pln"> </span><span class="pun">=&gt;</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="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 a </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rc</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">5</span><span class="pun">,</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Rc</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">

    println</span><span class="pun">!(</span><span class="str">"a initial rc count = {}"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">strong_count</span><span class="pun">(&amp;</span><span class="pln">a</span><span class="pun">));</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"a next item = {:?}"</span><span class="pun">,</span><span class="pln"> a</span><span class="pun">.</span><span class="pln">tail</span><span class="pun">());</span><span class="pln">

    let b </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rc</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">10</span><span class="pun">,</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">clone</span><span class="pun">(&amp;</span><span class="pln">a</span><span class="pun">))));</span><span class="pln">

    println</span><span class="pun">!(</span><span class="str">"a rc count after b creation = {}"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">strong_count</span><span class="pun">(&amp;</span><span class="pln">a</span><span class="pun">));</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"b initial rc count = {}"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">strong_count</span><span class="pun">(&amp;</span><span class="pln">b</span><span class="pun">));</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"b next item = {:?}"</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">.</span><span class="pln">tail</span><span class="pun">());</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> let </span><span class="typ">Some</span><span class="pun">(</span><span class="pln">link</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> a</span><span class="pun">.</span><span class="pln">tail</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="pun">*</span><span class="pln">link</span><span class="pun">.</span><span class="pln">borrow_mut</span><span class="pun">()</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">clone</span><span class="pun">(&amp;</span><span class="pln">b</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">"b rc count after changing a = {}"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">strong_count</span><span class="pun">(&amp;</span><span class="pln">b</span><span class="pun">));</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"a rc count after changing a = {}"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">strong_count</span><span class="pun">(&amp;</span><span class="pln">a</span><span class="pun">));</span><span class="pln">

    </span><span class="com">// ألغِ تعليق السطر التالي لتلاحظ وجود حلقة المرجع، إذ ستتسبب الشيفرة البرمجية بطفحان المكدس</span><span class="pln">
    </span><span class="com">// println!("a next item = {:?}", a.tail());</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 26: إنشاء حلقة مرجع بحيث تشير قيمتي <code>List</code> إلى بعضهما بعضًا
</p>

<p>
	أنشأنا نسخةً من <code>Rc&lt;List&gt;‎</code> تحتوي على القيمة <code>List</code> في المتغير <code>a</code> مع قائمة مبدئية تحتوي على القيم <code>5,Nil</code>، ثم أنشأنا نسخة <code>Rc&lt;List&gt;‎</code> أخرى تحتوي قيمة <code>List</code> أخرى في المتغير <code>b</code> تحوي القيمة 10 وتشير إلى القائمة في <code>a</code>.
</p>

<p>
	عدّلنا <code>a</code> لتشير إلى <code>b</code> بدلًا من <code>Nil</code> لنحصل بذلك على حلقة، وحقّقنا ذلك باستخدام التابع <code>tail</code> للحصول على مرجع إلى <code>RefCell&lt;Rc&lt;List&gt;&gt;‎</code> في <code>a</code>، والذي وضعناه بعد ذلك في المتغير <code>link</code>، ثم استخدمنا التابع <code>borrow_mut</code> على القيمة <code>RefCell&lt;Rc&lt;List&gt;&gt;‎</code> لتغيير القيمة داخل <code>Rc&lt;List&gt;‎</code> التي تخزّن القيمة <code>Nil</code> إلى القيمة <code>Rc&lt;List&gt;‎</code> الموجودة في <code>b</code>.
</p>

<p>
	نحصل على الخرج التالي عندما ننفذ هذه الشيفرة البرمجية مع الإبقاء على تعليمة <code>println!‎</code> الأخيرة معلّقة:
</p>

<pre class="ipsCode">$ cargo run
   Compiling cons-list v0.1.0 (file:///projects/cons-list)
    Finished dev [unoptimized + debuginfo] target(s) in 0.53s
     Running `target/debug/cons-list`
a initial rc count = 1
a next item = Some(RefCell { value: Nil })
a rc count after b creation = 2
b initial rc count = 1
b next item = Some(RefCell { value: Cons(5, RefCell { value: Nil }) })
b rc count after changing a = 2
a rc count after changing a = 2
</pre>

<p>
	يبلغ عدد مراجع نُسخ <code>Rc&lt;List&gt;‎</code> في كلٍّ من <code>a</code> و <code>b</code> القيمة 2 وذلك بعد تغيير القائمة في <code>a</code> لتشير إلى <code>b</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> في نهاية الدالة <code>main</code> المتغير <code>b</code> مما يغيّر من عدد مراجع نسخة <code>b</code> من <code>Rc&lt;List&gt;‎</code> من 2 إلى 1، ولن تُحرّر الذاكرة التي تشغلها <code>Rc&lt;List&gt;‎</code> على الكومة heap في هذه اللحظة لأن عدد المراجع هو 1 وليس 0، ومن ثم تُحرّر رست <code>a</code>، مما يُنقص عداد المرجع لنسخة <code>a</code> من <code>Rc&lt;List&gt;‎</code> من 2 إلى 1 أيضًا. لا يُمكن تحرير ذاكرة النسخة هذه لأن نسخة <code>Rc&lt;List&gt;‎</code> الأخرى لا تزال تشير إليها، وستبقى الذاكرة المحجوزة للقائمة شاغرة للأبد، ولتخيّل حلقة المرجع هذه بصريًا أنشأنا المخطط التالي في الشكل 4.
</p>

<p style="text-align: center;">
	<img alt="reference_cycle_of_lists_01.png" class="ipsImage ipsImage_thumbnailed" data-fileid="132176" data-unique="canc6nq4x" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2023_08/reference_cycle_of_lists_01.thumb.png.38879dbab52a3de58bb5dec197f808ea.png">
</p>

<p style="text-align: center;">
	الشكل 4: حلقة مرجع خاصة بقائمتين <code>a</code> و <code>b</code>، تشيران إلى بعضهما بعضًا
</p>

<p>
	إذا أزلت التعليق عن آخر تعليمة <code>println!‎</code> ونفّذت البرنامج، فستحاول رست طباعة الحلقة مع إشارة القائمة <code>a</code> إلى القائمة <code>b</code> وإشارتها بدورها إلى القائمة <code>a</code> وهكذا دواليك حتى يطفح المكدّس.
</p>

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

<p>
	إنشاء حلقات مرجع ليس بالأمر السهل ولكنه ليس مستحيلًا، فإذا كانت لديك قيم <code>RefCell&lt;T&gt;‎</code> تحتوي على قيم <code>Rc&lt;T&gt;‎</code> أو تشكيلات متداخلة مشابهة لأنواع مع قابلية التغيير الداخلي وعدّ المراجع، فيجب عليك التأكد أنك لم تنشئ حلقات، إذ لا يجب عليك الاعتماد على رست لإيجادها. إنشاء حلقة مرجع يحصل نتيجة خطأ منطقي في برنامجك ولذلك يجب عليك استخدام الاختبارات التلقائية ومراجعة الشيفرة البرمجية ووسائل تطوير البرامج الأخرى لتقليل احتمالية حدوثها.
</p>

<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="">علاقات الملكية ownership</a> وبعضها من علاقات لا ملكية non-ownership، بحيث تؤثر علاقات الملكية فقط إذا كانت القيمة ستُحرَّر.
</p>

<p>
	نريد من المتغاير <code>Cons</code> في الشيفرة 25 أن يملك قائمةً خاصةً به دائمًا، لذا فإن عملية إعادة تنظيم <span ipsnoautolink="true">هيكلية البيانات</span> ليست ممكنة. لنتابع مثالًا آخرًا باستخدام البيانات التخطيطية <a href="https://academy.hsoub.com/programming/advanced/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85-%D8%A7%D9%84%D8%AA%D8%AE%D8%B7%D9%8A%D8%B7%D9%8A%D8%A9-graphs-%D9%81%D9%8A-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-r1293/" rel="">graphs </a>بحيث تتكون من عُقد أب وعُقد أبناء لنرى كيف أن العلاقات اللا ملكية هي طريقة مناسبة لمنع حصول حلقات المرجع.
</p>

<h2>
	منع حلقات المرجع: بتحويل Rc<t>‎ إلى Weak<t>‎</t></t>
</h2>

<p>
	وضّحنا بحلول هذه النقطة أن استدعاء <code>Rc::clone</code> يزيد من قيمة <code>strong_count</code> الخاصة بالنسخة <code>Rc&lt;T&gt;‎</code> وأن نسخة <code>Rc&lt;T&gt;‎</code> تُحرَّر فقط عندما تكون قيمة <code>strong_count</code> هي 0. بإمكاننا أيضًا إنشاء مرجع ضعيف weak reference للقيمة داخل نسخة <code>Rc&lt;T&gt;‎</code> وذلك باستدعاء <code>Rc::downgrade</code> وتمرير مرجع إلى <code>Rc&lt;T&gt;‎</code>. المراجع القوية هي الطريقة التي يمكنك بها مشاركة الملكية لنسخة <code>Rc&lt;T&gt;‎</code>، بينما لا تعبّر المراجع الضعيفة عن علاقة ملكية، ولا يتأثر عددها عندما تُحرَّر نسخة من <code>Rc&lt;T&gt;‎</code>، ولا يسبب حلقة مرجعية، إذ ستُكسر الحلقات المتضمنة لمراجع ضعيفة عندما تصبح قيمة عدد المراجع القوية مساويةً إلى 0.
</p>

<p>
	نحصل على مؤشر ذكي من النوع <code>Weak&lt;T&gt;‎</code> عندما نستدعي <code>Rc::downgrade</code>، وبدلًا من زيادة <code>strong_count</code> في نسخة <code>Rc&lt;T&gt;‎</code> بقيمة 1، سيزيد استدعاء <code>Rc::downgrade</code> من قيمة <code>weak_count</code> بمقدار 1. يستخدم النوع <code>Rc&lt;T&gt;‎</code> القيمة <code>weak_count</code> لمتابعة عدد مراجع <code>Weak&lt;T&gt;‎</code> الموجودة بصورةٍ مشابهة للقيمة <code>strong_count</code>، إلا أن الفرق هنا هو أن <code>weak_count</code> لا يحتاج أن يكون 0 لكي تُنظف نسخة <code>&lt;Rc&lt;T</code>.
</p>

<p>
	يجب علينا التأكد أن القيمة موجودة فعلًا إذا أردت إجراء أي عمليات على القيمة التي يشير إليها <code>Weak&lt;T&gt;‎</code>، وذلك لأن القيمة قد تُحرَّر، ويمكننا التحقق من ذلك باستدعاء تابع <code>upgrade</code> على نسخة <code>Weak&lt;T&gt;‎</code> التي تعيد قيمةً من النوع <code>Option&lt;Rc&lt;T&gt;&gt;‎</code>، وسنحصل على نتيجة <code>Some</code> إذا لم تُحرَّر القيمة <code>Rc&lt;T&gt;‎</code> ونتيجة <code>None</code> إذا حُرّرت القيمة، تضمن رست أن حالتي <code>Some</code> و <code>None</code> ستُعامل على النحو الصحيح ولن تصبح مؤشرًا غير صالح، وذلك لأن <code>upgrade</code> تعيد <code>&lt;Option&lt;Rc&lt;T&gt;‎</code>.
</p>

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

<h2>
	إنشاء هيكل بيانات الشجرة يحتوي على عقدة مع عقد أبناء
</h2>

<p>
	أولًا، ننشئ شجرة مع عقد nodes تعرف عقد أبنائها، ولتحقيق ذلك ننشئ بنيةً ندعوها <code>Node</code> تحتوي على قيم من النوع <code>i32</code> إضافةً إلى مراجع تشير لقيم أبنائها <code>Node</code>:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6566_15" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">cell</span><span class="pun">::</span><span class="typ">RefCell</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">rc</span><span class="pun">::</span><span class="typ">Rc</span><span class="pun">;</span><span class="pln">

</span><span class="com">#[derive(Debug)]</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Node</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">
    children</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">&lt;</span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">Rc</span><span class="pun">&lt;</span><span class="typ">Node</span><span class="pun">&gt;&gt;&gt;,</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 leaf </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Node</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        value</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln">
        children</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="pln">vec</span><span class="pun">![]),</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    let branch </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Node</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        value</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln">
        children</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="pln">vec</span><span class="pun">![</span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">clone</span><span class="pun">(&amp;</span><span class="pln">leaf</span><span class="pun">)]),</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نريد من البنية <code>Node</code> أن تمتلك أبنائها <code>children</code>، كما نريد كذلك مشاركة الملكية مع المتغيرات لكي نتمكن من الوصول إلى كل <code>Node</code> في الشجرة مباشرةً، ومن أجل تحقيق ذلك سوف نعرّف عناصر <code>Vec&lt;T&gt;‎</code> بحيث تكون قيمًا من النوع <code>Rc&lt;Node&gt;‎</code>؛ كما نريد أيضًا تعديل العقد الأبناء لعقدة أخرى، لذلك سيكون لدينا <code>RefCell&lt;T&gt;‎</code> في الأبناء <code>children</code> وحول <code>Vec&lt;Rc&lt;Node&gt;&gt;‎</code>.
</p>

<p>
	سنستخدم تعريف الهيكلية لإنشاء نسخة <code>Node</code> واحدة بالاسم <code>leaf</code> وقيمتها 3 دون أن تحتوي على أبناء، ونسخةً أخرى اسمها <code>branch</code> قيمتها 5 تحتوي على <code>leaf</code> بمثابة واحد من أبنائها كما هو موضح في الشيفرة 27:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6566_17" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">cell</span><span class="pun">::</span><span class="typ">RefCell</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">rc</span><span class="pun">::</span><span class="typ">Rc</span><span class="pun">;</span><span class="pln">

</span><span class="com">#[derive(Debug)]</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Node</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">
    children</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">&lt;</span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">Rc</span><span class="pun">&lt;</span><span class="typ">Node</span><span class="pun">&gt;&gt;&gt;,</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 leaf </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Node</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        value</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln">
        children</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="pln">vec</span><span class="pun">![]),</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    let branch </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Node</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        value</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln">
        children</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="pln">vec</span><span class="pun">![</span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">clone</span><span class="pun">(&amp;</span><span class="pln">leaf</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;">
	الشيفرة 27: إنشاء عقدة <code>leaf</code> دون أبناء وعقدة <code>branch</code> مع <code>leaf</code> بمثابة واحد من أبنائها
</p>

<p>
	ننسخ القيمة <code>Rc&lt;Node&gt;‎</code> الموجودة في <code>leaf</code> ونخزّنها في <code>branch</code> وبذلك تصبح <code>Node</code> في <code>leaf</code> مُمتلكةً من قبل مالكين، هما <code>leaf</code> و <code>branch</code>. يمكننا الانتقال من <code>branch</code> إلى <code>leaf</code> عبر <code>branch.children</code> ولكن لا يوجد طريقة للوصول الى <code>leaf</code> من <code>branch</code>، وسبب ذلك هو أن <code>leaf</code> لا تمتلك مرجع إلى <code>branch</code>، وبالتالي لا تعرف بأنها مرتبطة مع <code>branch</code>، إذًا نحن بحاجة لأن تعرف أن <code>branch</code> هي العقدة الأب وهذا ما سنفعله.
</p>

<h2>
	إضافة مرجع يشير إلى عقدة ابن داخل عقدة أب
</h2>

<p>
	نحتاج لإضافة حقل <code>parent</code> لجعل عقدة ابن تعرف بوجود آبائها وذلك ضمن تعريف الهيكل <code>Node</code>. تكمن صعوبة الأمر في اختيار النوع الذي يجب استخدامه لتخزين القيمة <code>parent</code>، إلا أننا نعلم أنه لا يمكن للقيمة أن تحتوي النوع <code>Rc&lt;T&gt;‎</code> لأننا نحصل بذلك على حلقة مرجع تحتوي على القيمة <code>leaf.parent</code> مشيرةً إلى <code>branch</code> و <code>branch.children</code> مشيرةً إلى <code>leaf</code> مما يجعل قيم <code>strong_count</code> غير مساوية إلى القيمة 0 في أي من الحالات.
</p>

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

<p>
	لذا نجعل النوع <code>parent</code> يستخدم <code>Weak&lt;T&gt;‎</code> بدلًا من <code>Rc&lt;T&gt;‎</code> وتحديدًا النوع <code>RefCell&lt;Weak&lt;Node&gt;&gt;‎</code>، وسيصبح تعريف الهيكل <code>Node</code> على النحو التالي:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6566_19" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">cell</span><span class="pun">::</span><span class="typ">RefCell</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">rc</span><span class="pun">::{</span><span class="typ">Rc</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Weak</span><span class="pun">};</span><span class="pln">

</span><span class="com">#[derive(Debug)]</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Node</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">
    parent</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">&lt;</span><span class="typ">Weak</span><span class="pun">&lt;</span><span class="typ">Node</span><span class="pun">&gt;&gt;,</span><span class="pln">
    children</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">&lt;</span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">Rc</span><span class="pun">&lt;</span><span class="typ">Node</span><span class="pun">&gt;&gt;&gt;,</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 leaf </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Node</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        value</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln">
        parent</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Weak</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">()),</span><span class="pln">
        children</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="pln">vec</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">"leaf parent = {:?}"</span><span class="pun">,</span><span class="pln"> leaf</span><span class="pun">.</span><span class="pln">parent</span><span class="pun">.</span><span class="pln">borrow</span><span class="pun">().</span><span class="pln">upgrade</span><span class="pun">());</span><span class="pln">

    let branch </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Node</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        value</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln">
        parent</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Weak</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">()),</span><span class="pln">
        children</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="pln">vec</span><span class="pun">![</span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">clone</span><span class="pun">(&amp;</span><span class="pln">leaf</span><span class="pun">)]),</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    </span><span class="pun">*</span><span class="pln">leaf</span><span class="pun">.</span><span class="pln">parent</span><span class="pun">.</span><span class="pln">borrow_mut</span><span class="pun">()</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">downgrade</span><span class="pun">(&amp;</span><span class="pln">branch</span><span class="pun">);</span><span class="pln">

    println</span><span class="pun">!(</span><span class="str">"leaf parent = {:?}"</span><span class="pun">,</span><span class="pln"> leaf</span><span class="pun">.</span><span class="pln">parent</span><span class="pun">.</span><span class="pln">borrow</span><span class="pun">().</span><span class="pln">upgrade</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكن للعقدة أن تُشير إلى العقدة الأب ولكن لا يمكن أن تمتلكها. عدّلنا من الدالة <code>main</code> في الشيفرة 28 بحيث تستخدم التعريف الجديد لكي تكون للعقدة <code>leaf</code> طريقة للإشارة إلى العقدة الأب <code>branch</code>:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6566_21" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">cell</span><span class="pun">::</span><span class="typ">RefCell</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">rc</span><span class="pun">::{</span><span class="typ">Rc</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Weak</span><span class="pun">};</span><span class="pln">

</span><span class="com">#[derive(Debug)]</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Node</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">
    parent</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">&lt;</span><span class="typ">Weak</span><span class="pun">&lt;</span><span class="typ">Node</span><span class="pun">&gt;&gt;,</span><span class="pln">
    children</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">&lt;</span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">Rc</span><span class="pun">&lt;</span><span class="typ">Node</span><span class="pun">&gt;&gt;&gt;,</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 leaf </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Node</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        value</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln">
        parent</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Weak</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">()),</span><span class="pln">
        children</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="pln">vec</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">"leaf parent = {:?}"</span><span class="pun">,</span><span class="pln"> leaf</span><span class="pun">.</span><span class="pln">parent</span><span class="pun">.</span><span class="pln">borrow</span><span class="pun">().</span><span class="pln">upgrade</span><span class="pun">());</span><span class="pln">

    let branch </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Node</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        value</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln">
        parent</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Weak</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">()),</span><span class="pln">
        children</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="pln">vec</span><span class="pun">![</span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">clone</span><span class="pun">(&amp;</span><span class="pln">leaf</span><span class="pun">)]),</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    </span><span class="pun">*</span><span class="pln">leaf</span><span class="pun">.</span><span class="pln">parent</span><span class="pun">.</span><span class="pln">borrow_mut</span><span class="pun">()</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">downgrade</span><span class="pun">(&amp;</span><span class="pln">branch</span><span class="pun">);</span><span class="pln">

    println</span><span class="pun">!(</span><span class="str">"leaf parent = {:?}"</span><span class="pun">,</span><span class="pln"> leaf</span><span class="pun">.</span><span class="pln">parent</span><span class="pun">.</span><span class="pln">borrow</span><span class="pun">().</span><span class="pln">upgrade</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 28: عقدة <code>leaf</code> مع مرجع ضعيف للعقدة الأب <code>branch</code>
</p>

<p>
	يبدو إنشاء العقدة <code>leaf</code> شبيهًا للشيفرة 27، باختلاف الحقل <code>parent</code>، إذ تبدأ العقدة <code>leaf</code> بدون أب وهذا يمكّننا من إنشاء نسخة لمرجع <code>Weak&lt;Node&gt;‎</code> فارغ.
</p>

<p>
	عندما نحاول الحصول على مرجع للعقدة الأب الخاصة بالعقدة <code>leaf</code> وذلك باستخدام التابع <code>upgrade</code>، نحصل على قيمة <code>None</code>، ويمكن رؤية الخرج الناتج من أول تعليمة <code>println!‎</code>:
</p>

<pre class="ipsCode">leaf parent = None
</pre>

<p>
	نحصل على مرجع <code>Weak&lt;Node&gt;‎</code> جديد عندما نُنشئ العقدة <code>branch</code> وذلك في الحقل <code>parent</code> لأن <code>branch</code> لا تحتوي على عقدة أب. لا يزال لدينا <code>leaf</code> وهي عقدة ابن للعقدة <code>branch</code>، وحالما يوجد لدينا نسخة من <code>Node</code> في <code>branch</code> سيكون بإمكاننا تعديل <code>leaf</code> بمنحها مرجعًا إلى العقدة الأب الخاصة بها من النوع <code>Weak&lt;Node&gt;‎</code>. نستخدم التابع <code>borrow_mut</code> على النوع <code>RefCell&lt;Weak&lt;Node&gt;&gt;‎</code> في حقل <code>parent</code> الخاص بالعقدة <code>leaf</code>، ومن ثم نستخدم الدالة <code>Rc::downgrade</code> لإنشاء مرجع من النوع <code>Weak&lt;Node&gt;‎</code> يشير إلى العقدة <code>branch</code> من النوع <code>Rc&lt;Node&gt;‎</code> في <code>branch</code>.
</p>

<p>
	نحصل على متغاير <code>Some</code> يحتوي على <code>branch</code> عندما نطبع أب العقدة <code>leaf</code> مجددًا، إذ يمكن للعقدة <code>leaf</code> الآن الوصول إلى العقدة الأب. نستطيع أيضًا تفادي إنشاء الحلقة التي ستتسبب أخيرًا في طفحان المكدّس عند طباعة <code>leaf</code> كما هو الحال في الشيفرة 26؛ إذ تُطبع مراجع <code>Weak&lt;Node&gt;‎</code> مع الكلمة <code>(Weak)</code> جانبها:
</p>

<pre class="ipsCode">leaf parent = Some(Node { value: 5, parent: RefCell { value: (Weak) },
children: RefCell { value: [Node { value: 3, parent: RefCell { value: (Weak) },
children: RefCell { value: [] } }] } })
</pre>

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

<h2>
	مشاهدة التغييرات التي تحصل على strong_count و weak_count
</h2>

<p>
	دعنا نلاحظ كيف تتغير قيم نُسخ <code>Rc&lt;Node&gt;‎</code> لكل من النسخة <code>strong_count</code> و <code>weak_count</code>، وذلك عن طريق إنشاء نطاق داخلي جديد ونقل عملية إنشاء <code>branch</code> إلى هذا النطاق، إذ نستطيع بفعل ذلك مشاهدة ما الذي يحصل عند إنشاء العقدة <code>branch</code> وتحريرها بعد أن تخرج من النطاق. التعديلات موضحة في الشيفرة 29:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_6566_23" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">cell</span><span class="pun">::</span><span class="typ">RefCell</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">rc</span><span class="pun">::{</span><span class="typ">Rc</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Weak</span><span class="pun">};</span><span class="pln">

</span><span class="com">#[derive(Debug)]</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">Node</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">
    parent</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">&lt;</span><span class="typ">Weak</span><span class="pun">&lt;</span><span class="typ">Node</span><span class="pun">&gt;&gt;,</span><span class="pln">
    children</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">&lt;</span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">Rc</span><span class="pun">&lt;</span><span class="typ">Node</span><span class="pun">&gt;&gt;&gt;,</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 leaf </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Node</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        value</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln">
        parent</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Weak</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">()),</span><span class="pln">
        children</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="pln">vec</span><span class="pun">![]),</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    println</span><span class="pun">!(</span><span class="pln">
        </span><span class="str">"leaf strong = {}, weak = {}"</span><span class="pun">,</span><span class="pln">
        </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">strong_count</span><span class="pun">(&amp;</span><span class="pln">leaf</span><span class="pun">),</span><span class="pln">
        </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">weak_count</span><span class="pun">(&amp;</span><span class="pln">leaf</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 branch </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Node</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            value</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln">
            parent</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">Weak</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">()),</span><span class="pln">
            children</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="pln">vec</span><span class="pun">![</span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">clone</span><span class="pun">(&amp;</span><span class="pln">leaf</span><span class="pun">)]),</span><span class="pln">
        </span><span class="pun">});</span><span class="pln">

        </span><span class="pun">*</span><span class="pln">leaf</span><span class="pun">.</span><span class="pln">parent</span><span class="pun">.</span><span class="pln">borrow_mut</span><span class="pun">()</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">downgrade</span><span class="pun">(&amp;</span><span class="pln">branch</span><span class="pun">);</span><span class="pln">

        println</span><span class="pun">!(</span><span class="pln">
            </span><span class="str">"branch strong = {}, weak = {}"</span><span class="pun">,</span><span class="pln">
            </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">strong_count</span><span class="pun">(&amp;</span><span class="pln">branch</span><span class="pun">),</span><span class="pln">
            </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">weak_count</span><span class="pun">(&amp;</span><span class="pln">branch</span><span class="pun">),</span><span class="pln">
        </span><span class="pun">);</span><span class="pln">

        println</span><span class="pun">!(</span><span class="pln">
            </span><span class="str">"leaf strong = {}, weak = {}"</span><span class="pun">,</span><span class="pln">
            </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">strong_count</span><span class="pun">(&amp;</span><span class="pln">leaf</span><span class="pun">),</span><span class="pln">
            </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">weak_count</span><span class="pun">(&amp;</span><span class="pln">leaf</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">"leaf parent = {:?}"</span><span class="pun">,</span><span class="pln"> leaf</span><span class="pun">.</span><span class="pln">parent</span><span class="pun">.</span><span class="pln">borrow</span><span class="pun">().</span><span class="pln">upgrade</span><span class="pun">());</span><span class="pln">
    println</span><span class="pun">!(</span><span class="pln">
        </span><span class="str">"leaf strong = {}, weak = {}"</span><span class="pun">,</span><span class="pln">
        </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">strong_count</span><span class="pun">(&amp;</span><span class="pln">leaf</span><span class="pun">),</span><span class="pln">
        </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">weak_count</span><span class="pun">(&amp;</span><span class="pln">leaf</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;">
	الشيفرة 29: إنشاء <code>branch</code> في نطاق داخلي وفحص عدد المراجع القوية والضعيفة
</p>

<p>
	يبلغ عدد المراجع القوية للنوع <code>Rc&lt;Node&gt;‎</code> القيمة 1 بعد إنشاء <code>leaf</code>، وعدد المراجع الضعيفة يساوي 0. نُنشئ في النطاق الداخلي العقدة <code>branch</code> ونربطها مع <code>leaf</code> وهذه هي النقطة التي نبدأ فيها بطباعة عدد المراجع، يبلغ عدد المراجع القوية الخاصة بالنوع <code>Rc&lt;Node&gt;‎</code> في <code>branch</code> القيمة 1 وعدد المراجع الضعيفة 1 (لأن <code>leaf.parent</code> تشير إلى <code>branch</code> باستخدام قيمة من النوع <code>&lt;Weak&lt;Node</code>). نلاحظ تغيّر عداد المراجع القوية إلى 2 عندما نطبع عدد المراجع القوية والضعيفة في <code>leaf</code> وذلك لأن <code>branch</code> هي نسخةٌ من النوع <code>Rc&lt;Node&gt;‎</code> من القيمة <code>leaf</code> ومخزّنة في <code>branch.children</code>، إلا أننا ما زلنا نحصل على عدد مراجع ضعيفة يساوي 0.
</p>

<p>
	تخرج <code>branch</code> عن النطاق عندما ينتهي النطاق الداخلي وينقص عدد المراجع القوي الخاص بالنوع <code>Rc&lt;Node&gt;‎</code> إلى 0، لذا تُحرّر قيمة <code>Node</code> الخاصة به. لا يوجد تأثير لعدد المراجع الضعيفة البالغ 1 ضمن <code>leaf.parent</code> على تحرير القيمة <code>Node</code> أو عدم تحريرها ولذلك لا نحصل على تسريب للذاكرة.
</p>

<p>
	إذا أردنا الوصول إلى العقدة الأب الخاصة بالعقدة <code>leaf</code> في نهاية النطاق، فسنحصل على <code>None</code> مجددًا. عدد المراجع القوية الخاصة بالنوع <code>Rc&lt;Node&gt;‎</code> في <code>leaf</code> هو 1، وعدد المراجع الضعيفة هو 0 في نهاية البرنامج، وذلك لأن المتغير <code>leaf</code> هو المرجع الوحيد للنوع <code>Rc&lt;Node&gt;‎</code> مجددًا.
</p>

<p>
	المنطق الذي يُدير عدّ وتحرير القيم مُطبَّق في كلٍّ من <code>Rc&lt;T&gt;‎</code> و <code>Weak&lt;T&gt;‎</code>، إضافةً إلى تنفيذ السمة <code>Drop</code>. سيجعل تحديد أن علاقة العقدة الابن بالعقدة الأب يجب أن تكون مرجعًا ضعيفًا من النوع <code>Weak&lt;T&gt;‎</code> في تعريف <code>Node</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/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%84%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%B1%D8%B3%D8%AA-%D8%A8%D8%B5%D9%88%D8%B1%D8%A9-%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D8%A2%D9%86%D9%8A%D9%8B%D8%A7-r2043/" rel="">استخدام الخيوط Threads لتنفيذ شيفرات رست بصورة متزامنة آنيًا</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1-%D8%A7%D9%84%D8%B0%D9%83%D9%8A-refcell%E2%80%8E-%D9%88%D9%86%D9%85%D8%B7-%D9%82%D8%A7%D8%A8%D9%84%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%BA%D9%8A%D9%8A%D8%B1-%D8%A7%D9%84%D8%AF%D8%A7%D8%AE%D9%84%D9%8A-interior-mutability-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2037/" rel="">المؤشر الذكي Refcell<t>‎ ونمط قابلية التغيير الداخلي interior mutability في لغة رست Rust</t></a>
	</li>
	<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%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%D9%85%D8%A4%D8%B4%D8%B1-rc%E2%80%8E-%D8%A7%D9%84%D8%B0%D9%83%D9%8A-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D9%87-%D9%84%D9%84%D8%A5%D8%B4%D8%A7%D8%B1%D8%A9-%D8%A5%D9%84%D9%89-%D8%B9%D8%AF%D8%AF-%D8%A7%D9%84%D9%85%D8%B1%D8%A7%D8%AC%D8%B9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2036/" rel="">المؤشر Rc<t>‎ الذكي واستخدامه للإشارة إلى عدد المراجع في لغة رست Rust</t></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>
</ul>
]]></description><guid isPermaLink="false">2038</guid><pubDate>Wed, 02 Aug 2023 11:08:00 +0000</pubDate></item><item><title><![CDATA[المؤشر الذكي Refcell<T>‎ ونمط قابلية التغيير الداخلي interior mutability في لغة Rust]]></title><link>https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1-%D8%A7%D9%84%D8%B0%D9%83%D9%8A-refcell%E2%80%8E-%D9%88%D9%86%D9%85%D8%B7-%D9%82%D8%A7%D8%A8%D9%84%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%BA%D9%8A%D9%8A%D8%B1-%D8%A7%D9%84%D8%AF%D8%A7%D8%AE%D9%84%D9%8A-interior-mutability-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-rust-r2037/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_07/--------interior-mutability----Rust.png.00f66596b6666b19221d2b47a9583ac0.png" /></p>
<p>
	تُعد قابلية التغيير الداخلي interior mutability نمط تصميم في رست يسمح لك بتغيير البيانات حتى في حالة وجود مراجع ثابتة immutable تشير لتلك البيانات، ولا تسمح قواعد الاستعارة بهذا الإجراء عادًة. يستخدم النمط شيفرة "unsafe" لتغيير البيانات وذلك داخل <span ipsnoautolink="true">هيكل بيانات</span> للتحايل على قواعد رست المعتادة التي تتحكم بقابلية التعديل والاستعارة، إذ تشير الشيفرة غير الآمنة للمصرّف أننا نتحقق من القواعد يدويًا عوضًا عن الاعتماد على المصرّف للتحقق منها لنا، وسنناقش الشيفرة البرمجية غير الآمنة بالتفصيل لاحقًا.
</p>

<p>
	يمكننا استخدام الأنواع التي تستخدم نمط قابلية التغيير الداخلي فقط عندما نتأكد أن قواعد الاستعارة تُتّبع في وقت التنفيذ، على الرغم من أن المصرّف لا يمكنه ضمان ذلك. تُغلّف wrap الشيفرة غير الآمنة "unsafe" ضمن <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>
	لنكتشف هذا المفهوم من خلال النظر إلى نوع <code>&lt;RefCell&lt;T</code> الذي يتبع نمط التغيير الداخلي.
</p>

<h2>
	فرض قواعد الاستعارة عند وقت التنفيذ باستخدام &lt;RefCell&lt;T
</h2>

<p>
	يمثل النمط <code>&lt;RefCell&lt;T</code> بعكس <code>&lt;Rc&lt;T</code> ملكيةً مفردةً للبيانات التي يحملها. إذًا ما الذي يجعل <code>&lt;RefCell&lt;T</code> مختلفًا عن نمط مثل <code>&lt;Box&lt;T</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="">المراجع References والاستعارة Borrowing والشرائح Slices</a><span>:</span>
</p>

<ul>
	<li>
		يمكنك بأي وقت أن تمتلك إما مرجعًا متغيرًا واحدًا أو أي عدد من المراجع الثابتة ولكن ليس كليهما.
	</li>
	<li>
		يجب على المراجع أن تكون صالحة دومًا.
	</li>
</ul>

<p>
	تُفرض ثوابت قواعد الاستعارة borrowing rules’ invariants عند وقت <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>&lt;Box&lt;T</code>، وتُفرض هذه الثوابت مع <code>&lt;RefCell&lt;T</code> في وقت التنفيذ. نحصل على خطأ تصريفي مع المراجع إذا خرقنا هذه القواعد، إلا أنه في حالة <code>&lt;RefCell&lt;T</code> سيُصاب البرنامج بالهلع ويتوقف إذا خرقت القواعد ذاتها.
</p>

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

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

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

<p>
	وكما في <code>&lt;Rc&lt;T</code> تُستخدم <code>&lt;RefCell&lt;T</code> فقط في السيناريوهات ذات الخيوط المفردة single-threaded وستعطيك خطأً وقت التنفيذ إذا حاولت استخدامه في سياق متعدد الخيوط multithreaded. سنتحدث عن كيفية الحصول على وظيفة <code>&lt;RefCell&lt;T</code> في برنامج متعدد الخيوط لاحقًا.
</p>

<p>
	فيما يلي ملخص لأسباب اختيار<code>&lt;Box&lt;T</code> أو <code>&lt;Rc&lt;T</code> أو <code>&lt;RefCell&lt;T</code>:
</p>

<ul>
	<li>
		يمكّن النوع <code>&lt;Rc&lt;T</code> وجود عدّة مالكين لنفس البيانات بينما يوجد للنوعين <code>&lt;RefCell&lt;T</code> و <code>&lt;Box&lt;T</code> مالك وحيد.
	</li>
	<li>
		يسمح النوع <code>&lt;Box&lt;T</code> بوجود استعارات متغيّرة أو ثابتة يُتحقَّق منها وقت التصريف، بينما يسمح النوع <code>&lt;RefCell&lt;T</code> باستعارات متغيّرة أو ثابتة وقت التنفيذ.
	</li>
	<li>
		بما أن النوع <code>&lt;RefCell&lt;T</code> يسمح باستعارات متغيّرة مُتحقق منها في وقت التنفيذ، يمكنك تغيير القيمة داخل <code>&lt;RefCell&lt;T</code> حتى عندما يكون النوع <code>&lt;RefCell&lt;T</code> ثابتًا.
	</li>
</ul>

<p>
	تغيير القيمة داخل قيمة ثابتة هو نمط التغيير الداخلي. لنلقي نظرةً على موقف يكون فيه نمط التغيير الداخلي مفيدًا ونفحص كيف يكون ذلك ممكنًا.
</p>

<h2>
	التغيير الداخلي: استعارة متغيرة لقيمة ثابتة
</h2>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8691_6" 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">
    let y </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">mut x</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إذا حاولت تصريف الشيفرة السابقة فسيظهر لك الخطأ التالي:
</p>

<pre class="ipsCode">$ cargo run
   Compiling borrowing v0.1.0 (file:///projects/borrowing)
error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
 --&gt; src/main.rs:3:13
  |
2 |     let x = 5;
  |         - help: consider changing this to be mutable: `mut x`
3 |     let y = &amp;mut x;
  |             ^^^^^^ cannot borrow as mutable

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

<p>
	على الرغم من ذلك هناك حالات قد يكون من المفيد فيها لقيمة ما أن تغير نفسها باستخدام توابعها methods الخاصة مع جعلها ثابتة بالنسبة للشيفرات الأخرى، بحيث لا تستطيع الشيفرة البرمجية خارج توابع القيمة تغيير القيمة. يُعد استخدام <code>&lt;RefCell&lt;T</code> إحدى الطرق للحصول على التغيير الداخلي إلا أن النوع <code>&lt;RefCell&lt;T</code> لا يتغلب على كامل قواعد الاستعارة، إذ يسمح مدقق الاستعارة في المصرّف بالتغيير الداخلي ويجري التحقق من قواعد الاستعارة في وقت التنفيذ بدلًا من ذلك. إذا خُرقت القواعد فسوف تحصل على حالة هلع <code>panic!‎</code> بدلًا من خطأ تصريفي.
</p>

<p>
	لنعمل من خلال مثال عملي يمكّننا من استخدام <code>&lt;RefCell&lt;T</code> لتغيير قيمة ثابتة ومعرفة لماذا يُعد ذلك مفيدًا.
</p>

<h3>
	حالة استخدام للتغيير الداخلي: الكائنات الزائفة Mock Objects
</h3>

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

<p>
	لا تحتوي رست على <a href="https://academy.hsoub.com/programming/java/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-objects-%D9%88%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-oop-r966/" rel="">كائنات </a>بنفس معنى الكائنات في لغات البرمجة الأخرى، ولا تحتوي رست على قدرة التعامل مع الكائنات الزائفة مُضمَّنة في المكتبة القياسية كما تفعل بعض اللغات الأخرى، ومع ذلك يمكنك بالتأكيد إنشاء هيكل يخدم الهدف من الكائنات الزائفة.
</p>

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

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

<p>
	تظهر الشيفرة 20 الشيفرة البرمجية الخاصة بالمكتبة:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8691_8" style=""><span class="pln">pub trait </span><span class="typ">Messenger</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn send</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> msg</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">

pub </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">LimitTracker</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">:</span><span class="pln"> </span><span class="typ">Messenger</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    messenger</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="str">'</span><span class="pln">a T</span><span class="pun">,</span><span class="pln">
    value</span><span class="pun">:</span><span class="pln"> usize</span><span class="pun">,</span><span class="pln">
    max</span><span class="pun">:</span><span class="pln"> usize</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="str">'a, T&gt; LimitTracker&lt;'</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> T</span><span class="pun">&gt;</span><span class="pln">
</span><span class="kwd">where</span><span class="pln">
    T</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Messenger</span><span class="pun">,</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">messenger</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&amp;</span><span class="str">'a T, max: usize) -&gt; LimitTracker&lt;'</span><span class="pln">a</span><span class="pun">,</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">LimitTracker</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            messenger</span><span class="pun">,</span><span class="pln">
            value</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
            max</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 fn set_value</span><span class="pun">(&amp;</span><span class="pln">mut self</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">:</span><span class="pln"> usize</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">value </span><span class="pun">=</span><span class="pln"> value</span><span class="pun">;</span><span class="pln">

        let percentage_of_max </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">value as f64 </span><span class="pun">/</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">max as f64</span><span class="pun">;</span><span class="pln">

        </span><span class="kwd">if</span><span class="pln"> percentage_of_max </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">1.0</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            self</span><span class="pun">.</span><span class="pln">messenger</span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="str">"Error: You are over your quota!"</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"> percentage_of_max </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">0.9</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            self</span><span class="pun">.</span><span class="pln">messenger
                </span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="str">"Urgent warning: You've used up over 90% of your quota!"</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"> percentage_of_max </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">0.75</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            self</span><span class="pun">.</span><span class="pln">messenger
                </span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="str">"Warning: You've used up over 75% of your quota!"</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>Messenger</code> لها تابعٌ واحدٌ يدعى <code>send</code> يقبل مرجعًا ثابتًا يشير إلى <code>self</code> بالإضافة إلى نص الرسالة، وهذه السمة هي الواجهة التي يحتاج الكائن الزائف الخاص بنا إلى تنفيذها بحيث يمكن استخدام الكائن الزائف بنفس الطريقة التي يُستخدم بها الكائن الحقيقي؛ والجزء المهم الآخر هو أننا نريد اختبار سلوك التابع <code>set_value</code> على <code>LimitTracker</code>. يمكننا تغيير ما نمرّره لمعامل <code>value</code>، إلا أن <code>set_value</code> لا تُعيد لنا أي شيء لإجراء تأكيدات assertions عليه؛ إذ نريد أن نكون قادرين على القول أنه على المُرسل أن يرسل رسالة معيّنة إذا أنشأنا <code>LimitTracker</code> بشيء يطبّق السمة <code>Messenger</code> وقيمة معينة لـ <code>max</code>، عندما نمرر أرقامًا مختلفة مثل قيمة <code>value</code>.
</p>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8691_10" 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="kwd">struct</span><span class="pln"> </span><span class="typ">MockMessenger</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        sent_messages</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">

    impl </span><span class="typ">MockMessenger</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"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">MockMessenger</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">MockMessenger</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                sent_messages</span><span class="pun">:</span><span class="pln"> vec</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">

    impl </span><span class="typ">Messenger</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">MockMessenger</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        fn send</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> message</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">
            self</span><span class="pun">.</span><span class="pln">sent_messages</span><span class="pun">.</span><span class="pln">push</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="pln">message</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">#[test]</span><span class="pln">
    fn it_sends_an_over_75_percent_warning_message</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let mock_messenger </span><span class="pun">=</span><span class="pln"> </span><span class="typ">MockMessenger</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">();</span><span class="pln">
        let mut limit_tracker </span><span class="pun">=</span><span class="pln"> </span><span class="typ">LimitTracker</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(&amp;</span><span class="pln">mock_messenger</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">);</span><span class="pln">

        limit_tracker</span><span class="pun">.</span><span class="pln">set_value</span><span class="pun">(</span><span class="lit">80</span><span class="pun">);</span><span class="pln">

        assert_eq</span><span class="pun">!(</span><span class="pln">mock_messenger</span><span class="pun">.</span><span class="pln">sent_messages</span><span class="pun">.</span><span class="pln">len</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></pre>

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

<p>
	تعرِّف شيفرة الاختبار السابقة هيكل <code>MockMessenger</code> يحتوي على حقل <code>sent_messages</code> مع قيم <code>Vec</code> من نوع سلسلة نصية <code>String</code> لتتبع الرسائل المطلوب إرسالها؛ كما نعرّف أيضًا دالة مرتبطة associated function بالاسم <code>new</code> لتسهيل إنشاء قيم <code>MockMessenger</code> الجديدة التي تبدأ بلائحة فارغة من الرسائل، ثم نطبّق السمة <code>Messenger</code> على <code>MockMessenger</code> حتى نستطيع إعطاء ملكية <code>MockMessenger</code> لنسخة <code>LimitTracker</code>. نأخذ الرسالة الممرّرة مثل معامل في تعريف التابع <code>send</code> ونخزّنها في قائمة <code>MockMessenger</code> الخاصة بالحقل <code>sent_messages</code>.
</p>

<p>
	نختبر في الاختبار ما يحدث عندما يُطلب من <code>LimitTracker</code> تعيين <code>value</code> لشيء يزيد عن 75 بالمائة من <code>max</code>، إذ نُنشئ أولًا نسخةً جديدةً من <code>MockMessenger</code> تبدأ بقائمة فارغة من الرسائل، ثم نُنشئ <code>LimitTracker</code> جديد ونمرّر له مرجعًا يشير إلى <code>MockMessenger</code> الجديد وقيمة <code>max</code> هي 100. نستدعي التابع <code>set_value</code> على <code>LimitTracker</code> بقيمة 80 والتي تزيد عن 75 بالمئة من 100 ثم نتأكد أن قائمة الرسائل التي يتتبعها <code>MockMessenger</code> يجب أن تحتوي الآن على رسالة واحدة.
</p>

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

<pre class="ipsCode">$ cargo test
   Compiling limit-tracker v0.1.0 (file:///projects/limit-tracker)
error[E0596]: cannot borrow `self.sent_messages` as mutable, as it is behind a `&amp;` reference
  --&gt; src/lib.rs:58:13
   |
2  |     fn send(&amp;self, msg: &amp;str);
   |             ----- help: consider changing that to be a mutable reference: `&amp;mut self`
...
58 |             self.sent_messages.push(String::from(message));
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self` is a `&amp;` reference, so the data it refers to cannot be borrowed as mutable

For more information about this error, try `rustc --explain E0596`.
error: could not compile `limit-tracker` due to previous error
warning: build failed, waiting for other jobs to finish...
</pre>

<p>
	لا يمكننا تعديل <code>MockMessenger</code> لتتبع الرسائل لأن التابع <code>send</code> يأخذ مرجعًا ثابتًا يشير إلى <code>self</code>، كما لا يمكننا أيضًا تنفيذ الاقتراح في نص الخطأ الذي يدلنا على استخدام <code>‎&amp;mut self</code> بدلًا من ذلك لأن بصمة التابع <code>send</code> لن تتطابق مع بصمة تعريف السمة <code>Messenger</code> (ننصحك بقراءة رسالة الخطأ بنفسك).
</p>

<p>
	هذه هي الحالة التي يُمكن أن يساعد فيها نمط التغيير الداخلي، إذ سنخزن <code>sent_messages</code> داخل <code>&lt;RefCell&lt;T</code>، ومن ثم سيتمكن التابع <code>send</code> من تعديل <code>sent_messages</code> لتخزين الرسائل التي رأيناها، وتظهر الشيفرة 22 كيف يبدو التغيير:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8691_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">
    use std</span><span class="pun">::</span><span class="pln">cell</span><span class="pun">::</span><span class="typ">RefCell</span><span class="pun">;</span><span class="pln">

    </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">MockMessenger</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        sent_messages</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">&lt;</span><span class="typ">Vec</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">&gt;&gt;,</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    impl </span><span class="typ">MockMessenger</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"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">MockMessenger</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="typ">MockMessenger</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                sent_messages</span><span class="pun">:</span><span class="pln"> </span><span class="typ">RefCell</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="pln">vec</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">

    impl </span><span class="typ">Messenger</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">MockMessenger</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        fn send</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> message</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">
            self</span><span class="pun">.</span><span class="pln">sent_messages</span><span class="pun">.</span><span class="pln">borrow_mut</span><span class="pun">().</span><span class="pln">push</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="pln">message</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">#[test]</span><span class="pln">
    fn it_sends_an_over_75_percent_warning_message</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">

        assert_eq</span><span class="pun">!(</span><span class="pln">mock_messenger</span><span class="pun">.</span><span class="pln">sent_messages</span><span class="pun">.</span><span class="pln">borrow</span><span class="pun">().</span><span class="pln">len</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></pre>

<p style="text-align: center;">
	الشيفرة 22: استخدام <code>&lt;RefCell&lt;T</code> لتغيير قيمة داخلية بينما تكون القيمة الخارجية ثابتة
</p>

<p>
	أصبح حقل <code>sent_messages</code> الآن من النوع <code>&lt;&lt;RefCell&lt;Vec&lt;String</code> بدلًا من <code>&lt;Vec&lt;String</code>. نُنشئ في الدالة <code>new</code> نسخةً جديدةً من <code>&lt;&lt;RefCell&lt;Vec&lt;String</code> حول الشعاع الفارغ.
</p>

<p>
	لا يزال المعامل الأول يمثّل استعارة <code>self</code> ثابتة تتطابق مع تعريف السمة، ولتطبيق التابع <code>send</code> نستدعي <code>borrow_mut</code> على <code>&lt;&lt;RefCell&lt;Vec&lt;String</code> في <code>self.sent_messages</code> للحصول على مرجع متغيّر للقيمة داخل <code>&lt;&lt;RefCell&lt;Vec&lt;String</code> التي تمثّل الشعاع، ومن ثم يمكننا أن نستدعي <code>push</code> على المرجع المتغيّر الذي يشير إلى الشعاع لتتبع الرسائل المرسلة أثناء الاختبار.
</p>

<p>
	التغيير الأخير الذي يتعين علينا إجراؤه هو ضمن التأكيد assertion لمعرفة عدد العناصر الموجودة في الشعاع الداخلي، ولتحقيق ذلك نستدعي <code>borrow</code> على <code>&lt;&lt;RefCell&lt;Vec&lt;String</code> للحصول على مرجع ثابت يشير إلى الشعاع.
</p>

<p>
	الآن بعد أن رأيت كيفية استخدام المؤشر <code>&lt;RefCell&lt;T</code> لنتعمق في كيفية عمله.
</p>

<h3>
	تتبع عمليات الاستعارة وقت التنفيذ عن طريق <code>&lt;RefCell&lt;T</code>
</h3>

<p>
	نستخدم عند إنشاء مراجع متغيّرة وثابتة الصيغة <code>&amp;</code> و <code>&amp; mut</code> على التوالي، بينما نستخدم في <code>&lt;RefCell&lt;T</code> التابعين <code>borrow</code> و <code>borrow_mut</code> اللذين يعدّان جزءًا من واجهة برمجية آمنة تنتمي إلى <code>&lt;RefCell&lt;T</code>. يُعيد التابع <code>borrow</code> نوع المؤشر الذكي <code>&lt;Ref&lt;T</code> ويُعيد التابع <code>borrow_mut</code> نوع المؤشر الذكي <code>&lt;RefMut&lt;T</code>، وينفّذ كلا النوعين السمة <code>Deref</code> لذلك يمكننا معاملتهما على أنهما مراجع نمطيّة regular references.
</p>

<p>
	يتعقّب المؤشر <code>&lt;RefCell&lt;T</code> عدد المؤشرات الذكية النشطة حاليًا من النوعين <code>&lt;Ref&lt;T</code> و <code>&lt;RefMut&lt;T</code>، وفي كل مرة نستدعي فيها التابع <code>borrow</code> يزيد المؤشر <code>&lt;RefCell&lt;T</code> من عدد عمليات الاستعارة النشطة الثابتة، وعندما تخرج قيمة من النوع <code>&lt;Ref&lt;T</code> عن النطاق، ينخفض عدد الاستعارات الثابتة بمقدار واحد. يتيح لنا المؤشر <code>&lt;RefCell&lt;T</code> الحصول على العديد من الاستعارات الثابتة أو استعارة واحدة متغيّرة في أي وقت تمامًا مثل قواعد الاستعارة وقت التصريف.
</p>

<p>
	إذا حاولنا انتهاك هذه القواعد، سيهلع تنفيذ <code>&lt;RefCell&lt;T</code> وقت التنفيذ بدلًا من الحصول على خطأ تصريفي كما اعتدنا حصوله مع المراجع. تظهر الشيفرة 23 تعديلًا في تطبيق الدالة <code>send</code> من الشيفرة 22، ونحاول هنا إنشاء استعارتين نشطتين متغيّرتين لنفس النطاق عمدًا لتوضيح أن <code>&lt;RefCell&lt;T</code> سيمنعنا من فعل ذلك وقت التنفيذ.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8691_14" style=""><span class="pln">    impl </span><span class="typ">Messenger</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">MockMessenger</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        fn send</span><span class="pun">(&amp;</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> message</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 mut one_borrow </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">sent_messages</span><span class="pun">.</span><span class="pln">borrow_mut</span><span class="pun">();</span><span class="pln">
            let mut two_borrow </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">sent_messages</span><span class="pun">.</span><span class="pln">borrow_mut</span><span class="pun">();</span><span class="pln">

            one_borrow</span><span class="pun">.</span><span class="pln">push</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="pln">message</span><span class="pun">));</span><span class="pln">
            two_borrow</span><span class="pun">.</span><span class="pln">push</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="pln">message</span><span class="pun">));</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	[الشيفرة 23: إنشاء مرجعين متغيّرين في النطاق ذاته لملاحظة هلع <code>&lt;RefCell&lt;T&gt;</code>]
</p>

<p>
	نُنشئ متغير اسمه <code>one_borrow</code> للمؤشر الذكي <code>&lt;RefMut&lt;T</code> الذي أُعيد من <code>borrow_mut</code>، ثم نُنشئ استعارةً متغيّرة أخرى بنفس الطريقة في المتغير <code>two_borrow</code>، مما يؤدي إلى إنشاء مرجعين متغيّرين في النطاق ذاته وهو أمر غير مسموح به.
</p>

<p>
	عندما ننفذ الاختبارات لمكتبتنا، تُصرَّف الشيفرة البرمجية الموجودة في الشيفرة 23 دون أي أخطاء إلا أن الاختبار سيفشل:
</p>

<pre class="ipsCode">$ cargo test
   Compiling limit-tracker v0.1.0 (file:///projects/limit-tracker)
    Finished test [unoptimized + debuginfo] target(s) in 0.91s
     Running unittests src/lib.rs (target/debug/deps/limit_tracker-e599811fa246dbde)

running 1 test
test tests::it_sends_an_over_75_percent_warning_message ... FAILED

failures:

---- tests::it_sends_an_over_75_percent_warning_message stdout ----
thread 'main' panicked at 'already borrowed: BorrowMutError', src/lib.rs:60:53
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::it_sends_an_over_75_percent_warning_message

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>already borrowed: BorrowMutError</code>، وهذه هي الطريقة التي يتعامل بها المؤشر <code>&lt;RefCell&lt;T</code> مع انتهاكات قواعد الاستعارة عند وقت التنفيذ.
</p>

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

<h2>
	وجود عدة مالكين للبيانات المتغيرة عن طريق استخدام كل من &lt;Rc &lt;T و &lt;RefCell&lt;T
</h2>

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

<p>
	على سبيل المثال تذكر مثال قائمة البنية في الشيفرة 18 <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1-rc%E2%80%8E-%D8%A7%D9%84%D8%B0%D9%83%D9%8A-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D9%87-%D9%84%D9%84%D8%A5%D8%B4%D8%A7%D8%B1%D8%A9-%D8%A5%D9%84%D9%89-%D8%B9%D8%AF%D8%AF-%D8%A7%D9%84%D9%85%D8%B1%D8%A7%D8%AC%D8%B9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2036/" rel="">في المقالة السابقة</a>، إذ استخدمنا <code>&lt;Rc&lt;T</code> للسماح لقوائم متعددة بمشاركة ملكية قائمة أخرى، ونظرًا لأن <code>&lt;Rc&lt;T</code> يحتوي على قيم ثابتة فقط، فلا يمكننا تغيير أي من القيم الموجودة في القائمة بمجرد إنشائها. دعنا نضيف <code>&lt;RefCell&lt;T</code> لاكتساب القدرة على تغيير القيم في القوائم. تُظهر الشيفرة 24 أنه يمكننا تعديل القيم المخزّنة في جميع القوائم وذلك باستخدام <code>&lt;RefCell&lt;T</code> داخل تعريف <code>Cons</code>:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8691_16" style=""><span class="com">#[derive(Debug)]</span><span class="pln">
</span><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="typ">Rc</span><span class="pun">&lt;</span><span class="typ">RefCell</span><span class="str">&lt;i32&gt;</span><span class="pun">&gt;,</span><span class="pln"> </span><span class="typ">Rc</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">
use std</span><span class="pun">::</span><span class="pln">cell</span><span class="pun">::</span><span class="typ">RefCell</span><span class="pun">;</span><span class="pln">
use std</span><span class="pun">::</span><span class="pln">rc</span><span class="pun">::</span><span class="typ">Rc</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 value </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">RefCell</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">

    let a </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rc</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="typ">Rc</span><span class="pun">::</span><span class="pln">clone</span><span class="pun">(&amp;</span><span class="pln">value</span><span class="pun">),</span><span class="pln"> </span><span class="typ">Rc</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">

    let b </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Cons</span><span class="pun">(</span><span class="typ">Rc</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">RefCell</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="lit">3</span><span class="pun">)),</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">clone</span><span class="pun">(&amp;</span><span class="pln">a</span><span class="pun">));</span><span class="pln">
    let c </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Cons</span><span class="pun">(</span><span class="typ">Rc</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="typ">RefCell</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="lit">4</span><span class="pun">)),</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">clone</span><span class="pun">(&amp;</span><span class="pln">a</span><span class="pun">));</span><span class="pln">

    </span><span class="pun">*</span><span class="pln">value</span><span class="pun">.</span><span class="pln">borrow_mut</span><span class="pun">()</span><span class="pln"> </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">"a after = {:?}"</span><span class="pun">,</span><span class="pln"> a</span><span class="pun">);</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"b after = {:?}"</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">);</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"c after = {:?}"</span><span class="pun">,</span><span class="pln"> c</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 24: استخدام <code>&lt;&lt;Rc&lt;RefCell&lt;i32</code> لإنشاء <code>List</code> يمكن تغييرها
</p>

<p>
	نُنشئ قيمة بحيث تكون نسخةً من النوع <code>&lt;&lt;Rc&lt;RefCell&lt;i32</code> ونخزنها في متغير باسم <code>value</code> حتى نتمكن من الوصول إليها مباشرةً لاحقًا، ثم نُنشئ <code>List</code> في <code>a</code> مع متغاير <code>Cons</code> يحمل <code>value</code>، نحتاج هنا إلى استنساخ <code>value</code> بحيث يكون لكل من <code>a</code> و <code>value</code> ملكية للقيمة الداخلية 5 بدلًا من نقل الملكية من <code>value</code> إلى <code>a</code> أو استعارة <code>a</code> من <code>value</code>.
</p>

<p>
	نغلّف القائمة <code>a</code> داخل <code>&lt;Rc&lt;T</code> عند إنشاء القائمتين <code>a</code> و <code>b</code> بحيث يمكن لكلّ من القائمتين الرجوع إلى <code>a</code> وهو ما فعلناه في الشيفرة 18 سابقًا.
</p>

<p>
	نريد إضافة القيمة 10 إلى <code>value</code> بعد إنشاء القوائم في <code>a</code> و <code>b</code> و <code>c</code>، ونحقّق ذلك عن طريق استدعاء <code>borrow_mut</code> على <code>value</code> التي تستخدم ميزة التحصيل التلقائي automatic differencing التي ناقشناها سابقًا في المقال <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</a> ضمن فقرة بعنوان (أين العامل <code>‎-&gt;‎</code>؟) لتحصيل <code>&lt;Rc&lt;T</code> إلى قيمة <code>&lt;RefCell&lt;T</code> الداخلية. يُعيد التابع <code>borrow_mut</code> المؤشر الذكي <code>&lt;RefMut&lt;T</code>، ومن ثم نستخدم عامل التحصيل عليه ونغير القيمة الداخلية.
</p>

<p>
	عندما ننفذ <code>a</code> و <code>b</code> و <code>c</code>، يمكننا أن نرى أن لجميعهم القيمة المعدلة 15 بدلًا من 5:
</p>

<pre class="ipsCode">$ cargo run
   Compiling cons-list v0.1.0 (file:///projects/cons-list)
    Finished dev [unoptimized + debuginfo] target(s) in 0.63s
     Running `target/debug/cons-list`
a after = Cons(RefCell { value: 15 }, Nil)
b after = Cons(RefCell { value: 3 }, Cons(RefCell { value: 15 }, Nil))
c after = Cons(RefCell { value: 4 }, Cons(RefCell { value: 15 }, Nil))
</pre>

<p>
	يا لهذه الطريقة الجميلة، فقد أصبح لدينا قيمة <code>List</code> ثابتة خارجيًا باستخدام <code>&lt;RefCell&lt;T&gt;</code>، إلا أنه يمكننا استخدام التوابع الموجودة في <code>&lt;RefCell&lt;T</code> التي توفر الوصول إلى قابلية التغيير الداخلية حتى نتمكن من تعديل بياناتنا عندما نحتاج إلى ذلك. تحمينا عمليات التحقق وقت التنفيذ لقواعد الاستعارة من حالات تسابق البيانات data race وفي بعض الأحيان يكون الأمر مستحقًا لمقايضة القليل من السرعة مقابل هذه المرونة في هياكل البيانات التي لدينا. لاحظ أن <code>&lt;RefCell&lt;T</code> لا يعمل مع الشيفرة متعددة الخيوط، ويعدّ <code>&lt;Mutex&lt;T</code> الإصدار الآمن من سلسلة <code>&lt;RefCell&lt;T</code>، وسنناقش <code>&lt;Mutex&lt;T</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/%D8%AD%D9%84%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-reference-cycles-%D9%88%D8%AA%D8%B3%D8%A8%D8%A8%D9%87%D8%A7-%D8%A8%D8%AA%D8%B3%D8%B1%D9%8A%D8%A8-%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-memory-leak-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2038/" rel="">حلقات المرجع Reference Cycles وتسببها بتسريب الذاكرة Memory Leak في لغة رست</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1-rc%E2%80%8E-%D8%A7%D9%84%D8%B0%D9%83%D9%8A-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D9%87-%D9%84%D9%84%D8%A5%D8%B4%D8%A7%D8%B1%D8%A9-%D8%A5%D9%84%D9%89-%D8%B9%D8%AF%D8%AF-%D8%A7%D9%84%D9%85%D8%B1%D8%A7%D8%AC%D8%B9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2036/" rel="">المؤشر <code>Rc&lt;T&gt;‎</code> الذكي واستخدامه للإشارة إلى عدد المراجع في لغة رست Rust</a>
	</li>
	<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%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>
	<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>
</ul>
]]></description><guid isPermaLink="false">2037</guid><pubDate>Mon, 31 Jul 2023 11:05:00 +0000</pubDate></item><item><title><![CDATA[المؤشر Rc<T>‎ الذكي واستخدامه للإشارة إلى عدد المراجع في لغة رست Rust]]></title><link>https://academy.hsoub.com/programming/rust/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1-rc%E2%80%8E-%D8%A7%D9%84%D8%B0%D9%83%D9%8A-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D9%87-%D9%84%D9%84%D8%A5%D8%B4%D8%A7%D8%B1%D8%A9-%D8%A5%D9%84%D9%89-%D8%B9%D8%AF%D8%AF-%D8%A7%D9%84%D9%85%D8%B1%D8%A7%D8%AC%D8%B9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2036/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_07/-Rc-_----------Rust.png.921c7b66e19512be75f3583cb6c4bf57.png" /></p>
<p>
	مبدأ الملكية ownership واضح في معظم الحالات، إذ تسمح لك الملكية بمعرفة أي متغير يملك قيمةً ما، ولكن هناك حالات تكون فيها القيمة الواحدة مملوكةً من أكثر من مالك، فمثلًا في شعبة<span ipsnoautolink="true"> هيكل البيانات</span> graph data structure قد تؤشر العديد من الأضلع edges إلى العقدة node ذاتها، وبالتالي تمتلك هذه العقدة كل الأضلع التي تشير إليها. لا يجب تحرير العقدة من الذاكرة إلا في حال عدم وجود أي ضلع يشير إليها وبالتالي عدم امتلاكها من قبل أحد.
</p>

<p>
	يمكنك تمكين وجود عدة مالكين صراحةً باستخدام النوع <code>Rc&lt;T&gt;‎</code> وهو اختصار لعدّ المرجع reference counting، إذ يُحصي النوع <code>Rc&lt;T&gt;‎</code> الخاص برست عدد المراجع التي تشير إلى قيمة محددة لتحديد فيما إذا كانت تلك القيمة قيد الاستخدام أم لا، وإذا لم يكن هناك أي مراجع تشير للقيمة، عندها يمكن تحرير القيمة دون التسبب بجعل أي مرجع غير صالح.
</p>

<p>
	تخيل <code>Rc&lt;T&gt;‎</code> مثل تلفاز في غرفة الجلوس، فعندما يدخل شخص الغرفة ليشاهد التلفاز يشغله، كما يمكن لآخرين القدوم للغرفة والمشاهدة أيضَا، وعندما يغادر آخر شخص الغرفة يطفئ التلفاز لأنه لم يعد يُستخدم، ولكن إذا أطفأ أحدهم التلفاز بينما يشاهده الآخرون فسيغضب الذين يشاهدون التلفاز.
</p>

<p>
	نستخدم النوع <code>Rc&lt;T&gt;‎</code> عندما نريد وضع بعض البيانات في الكومة heap بحيث يقرؤها عدة أجزاءٌ مختلفة من برنامجنا ولا نستطيع معرفة أي الأجزاء سينتهي من استخدام هذه البيانات أخيرًا عند تصريف البرنامج. نستطيع جعل الجزء الأخير مالك البيانات إذا كنّا نعرفه وبذلك تُطبَّق قواعد الملكية الاعتيادية لرست عند وقت <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>

<p>
	لاحظ أن <code>Rc&lt;T&gt;‎</code> موجود فقط للاستخدام في حالات استخدام <span ipsnoautolink="true">الخيط الواحد single-threaded</span>، وسنتحدث عن عدّ المراجع في البرامج ذات الخيوط المتعددة multithreaded لاحقًا عندما نتحدث عن التزامن concurrency.
</p>

<h2>
	استخدام Rc<t>‎ لمشاركة البيانات</t>
</h2>

<p>
	لنعاود النظر إلى قائمة البنية cons list في الشيفرة 5 من مقال <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="">المؤشرات الذكية</a> السابق، تذكّر أننا عرفنا القائمة باستخدام <code>Box&lt;T‎&gt;‎</code> إلا أننا سنُنشئ هذه المرة لائحتين يتشاركان ملكية قائمة ثالثة كما يوضح الشكل 3.
</p>

<p style="text-align: center;">
	<img alt="15-03.png" class="ipsImage ipsImage_thumbnailed" data-fileid="131507" data-unique="a0f3gb300" src="https://academy.hsoub.com/uploads/monthly_2023_07/15-03.png.ae3ed56f6c07c3ce03ee05bbab325865.png">
</p>

<p style="text-align: center;">
	الشكل 3: قائمة <code>b</code> وقائمة <code>c</code> يتشاركان ملكية قائمة ثالثة <code>a</code>
</p>

<p>
	ننشئ القائمة <code>a</code> التي تحتوي على 5 وبعدها 10، ومن ثم ننشئ قائمتين؛ قائمة <code>b</code> تبدأ بالقيمة 3 وقائمة <code>c</code> تبدأ بالقيمة 4، إذ ستستمر قيم كل من القائمتين <code>b</code> و <code>c</code> ضمن القائمة الثالثة <code>a</code> التي تحتوي على 5 و10، أي ستتشارك القائمتان القائمة الأولى التي تحتوي على 5 و10.
</p>

<p>
	لن تنجح محاولة تنفيذ هذه الحالة باستخدام تعريفنا للنوع <code>List</code> مع النوع <code>Box&lt;T&gt;‎</code> كما هو موضح في الشيفرة 17:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1092_6" 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 a </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Cons</span><span class="pun">(</span><span class="lit">5</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">10</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">
    let b </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">Box</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="pln">a</span><span class="pun">));</span><span class="pln">
    let c </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Cons</span><span class="pun">(</span><span class="lit">4</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="pln">a</span><span class="pun">));</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 17: شيفرة توضّح أنه ليس من المسموح استخدام قائمتين النوع <code>Box&lt;T&gt;‎</code> مع مشاركة ملكيتهما لقائمة ثالثة
</p>

<p>
	عندما نصرّف الشيفرة السابقة نحصل على هذا الخطأ:
</p>

<pre class="ipsCode" id="ips_uid_1795_7">$ cargo run
   Compiling cons-list v0.1.0 (file:///projects/cons-list)
error[E0382]: use of moved value: `a`
  --&gt; src/main.rs:11:30
   |
9  |     let a = Cons(5, Box::new(Cons(10, Box::new(Nil))));
   |         - move occurs because `a` has type `List`, which does not implement the `Copy` trait
10 |     let b = Cons(3, Box::new(a));
   |                              - value moved here
11 |     let c = Cons(4, Box::new(a));
   |                              ^ value used here after move

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

<p>
	تمتلك متغايرات <code>Cons</code> البيانات التي تحتفظ بها، لذا عندما ننشئ القائمة <code>b</code> تنتقل القائمة <code>a</code> إلى القائمة <code>b</code> وتمتلك <code>b</code> القائمة <code>a</code>، ثم عندما نحاول استخدام <code>a</code> مجددًا عند إنشاء <code>c</code> لا يُسمح لنا، وذلك لأن القائمة <code>a</code> نُقلت.
</p>

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

<p>
	عوضاً عن ذلك سنغير تعريف <code>List</code> لتستخدم <code>Rc&lt;T&gt;‎</code> بدلاً من <code>Box&lt;T&gt;‎</code> كما هو موضح في الشيفرة 18. يحفظ كل متغاير <code>Cons</code> قيمةً بالإضافة لمؤشر <code>Rc&lt;T&gt;‎</code> يشير إلى <code>List</code>، عندما ننشئ <code>b</code> بدلًا من أخذ ملكية <code>a</code>، سننسخ <code>Rc&lt;List&gt;‎</code> التي يحتفظ بها <code>a</code> وبذلك نزيد عدد المراجع من 1 إلى 2 ونجعل القائمة <code>a</code> والقائمة <code>b</code> تتشارك ملكية البيانات في <code>Rc&lt;List&gt;‎</code>، كما سننسخ أيضًا <code>a</code> عندما ننشئ <code>c</code>، وبذلك يزيد عدد المراجع من 2 إلى 3 مراجع، وفي كل مرة نستدعي <code>Rc::clone</code> سيزيد عدد المراجع للبيانات داخل <code>Rc&lt;List&gt;‎</code> ولن تُحرَّر البيانات إلا إذا لم يكن هناك أي مرجع يشير إليها.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1092_10" 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">Rc</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">
use std</span><span class="pun">::</span><span class="pln">rc</span><span class="pun">::</span><span class="typ">Rc</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 a </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rc</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">5</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Rc</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">10</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Rc</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">
    let b </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">Rc</span><span class="pun">::</span><span class="pln">clone</span><span class="pun">(&amp;</span><span class="pln">a</span><span class="pun">));</span><span class="pln">
    let c </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Cons</span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">clone</span><span class="pun">(&amp;</span><span class="pln">a</span><span class="pun">));</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 18: تعريف <code>List</code> التي تستخدم <code>Rc&lt;T&gt;‎</code>
</p>

<p>
	نحتاج لإضافة تعليمة <code>use</code> وذلك لإضافة <code>Rc&lt;T&gt;‎</code> إلى النطاق لأنه غير موجود في البداية. أنشأنا في الدالة <code>main</code> قائمة تحتوي على 5 و10 وخزّناها في قيمة من نوع <code>Rc&lt;List&gt;‎</code> جديدة ضمن <code>a</code>، ومن ثمّ استدعينا الدالة <code>Rc::clone</code> بعد أن أنشأنا <code>b</code> و <code>c</code> ومررنا مرجعًا مثل وسيط argument يشير إلى <code>Rc&lt;List&gt;‎</code> في <code>a</code>.
</p>

<p>
	يمكننا استدعاء <code>a.clone()‎</code> بدلًا من <code>Rc::clone(&amp;a)‎</code>، إلا أن الطريقة الاصطلاحية في رست تستخدم <code>Rc::clone</code> في هذه الحالة. لا ينسخ تنفيذ <code>Rc::clone</code> نسخةً فعليةً deep copy للبيانات مثل باقي تنفيذات أنواع <code>clone</code> الأخرى، إذ يزيد استدعاء الدالة <code>Rc::clone</code> عدد المراجع وهو ما لا يأخذ وقتًا طويلًا، بينما يأخذ النسخ الفعلي للبيانات وقتًا طويلًا، إلا أنه يمكننا التمييز بصريًا بين النسخ الفعلي والنسخ الذي يزيد عدد المراجع باستخدام الدالة <code>Rc::clone</code>. نحتاج لأخذ موضوع النسخ الفعلية بالحسبان عندما نبحث عن مشاكل متعلقة بأداء الشيفرة البرمجية وذلك بإهمال استدعاءات <code>Rc::clone</code>.
</p>

<h2>
	نسخ قيمة من النوع Rc<t>‎ يزيد عدد المراجع</t>
</h2>

<p>
	لنغير مثالنا الموجود في الشيفرة 18 بحيث يمكننا رؤية تغيّر عدد المراجع عندما ننشئ ونحرّر مرجعًا إلى <code>Rc&lt;T&gt;‎</code> في <code>a</code>.
</p>

<p>
	نغيّر من الدالة <code>main</code> في الشيفرة 19 بحيث تحتوي على نطاق داخلي حول القائمة <code>c</code> لكي نستطيع ملاحظة تغيّر قيمة عداد المراجع عندما تخرج <code>c</code> خارج النطاق.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1092_12" 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">Rc</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">
use std</span><span class="pun">::</span><span class="pln">rc</span><span class="pun">::</span><span class="typ">Rc</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 a </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Rc</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">5</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Rc</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">10</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Rc</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">
    println</span><span class="pun">!(</span><span class="str">"count after creating a = {}"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">strong_count</span><span class="pun">(&amp;</span><span class="pln">a</span><span class="pun">));</span><span class="pln">
    let b </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">Rc</span><span class="pun">::</span><span class="pln">clone</span><span class="pun">(&amp;</span><span class="pln">a</span><span class="pun">));</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"count after creating b = {}"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">strong_count</span><span class="pun">(&amp;</span><span class="pln">a</span><span class="pun">));</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        let c </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Cons</span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">clone</span><span class="pun">(&amp;</span><span class="pln">a</span><span class="pun">));</span><span class="pln">
        println</span><span class="pun">!(</span><span class="str">"count after creating c = {}"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">strong_count</span><span class="pun">(&amp;</span><span class="pln">a</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">"count after c goes out of scope = {}"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Rc</span><span class="pun">::</span><span class="pln">strong_count</span><span class="pun">(&amp;</span><span class="pln">a</span><span class="pun">));</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	الشيفرة 19: طباعة عدد المراجع
</p>

<p>
	نطبع عدد المراجع في كل نقطة يتغير فيه قيمته ضمن البرنامج، ونحصل على تلك القيمة باستدعاء الدالة <code>Rc::strong_count</code>. نسمّي الدالة <code>strong_count</code> بدلًا من <code>count</code>، وذلك لأن النوع <code>Rc&lt;T&gt;‎</code> يحتوي أيضًا على <code>weak_count</code>، وسنستخدم <code>weak_count</code> في مقال لاحق عندما نتحدث عن منع دورات المراجع وتحويل <code>Rc&lt;T&gt;‎</code> إلى <code>Weak&lt;T&gt;‎</code>. تطبع الشيفرة السابقة ما يلي:
</p>

<pre class="ipsCode" id="ips_uid_1795_9">$ cargo run
   Compiling cons-list v0.1.0 (file:///projects/cons-list)
    Finished dev [unoptimized + debuginfo] target(s) in 0.45s
     Running `target/debug/cons-list`
count after creating a = 1
count after creating b = 2
count after creating c = 3
count after c goes out of scope = 2</pre>

<p>
	نلاحظ أن <code>Rc&lt;List&gt;‎</code> في <code>a</code> تحتوي على عدد مراجع مبدئي يساوي إلى 1، ثم يزداد العدد كل مرة نستدعي فيها <code>clone</code> بمقدار 1، وعندما تخرج <code>c</code> خارج النطاق ينقص العدد بمقدار 1. لسنا بحاجة لاستدعاء الدالة لإنقاص عدد المراجع كما نفعل عند استدعاء <code>Rc::clone</code> بهدف زيادة عدد المراجع، إذ يُنقص تنفيذ سمة <code>drop</code> من عدد المراجع تلقائيًا عندما تخرج قيمة <code>Rc&lt;T&gt;‎</code> من النطاق.
</p>

<p>
	ما لا نستطيع رؤيته في هذا المثال هو خروج <code>a</code> من النطاق بعد خروج <code>b</code> في نهاية الدالة <code>main</code>، ويُضبط عدد المراجع فيما بعد إلى القيمة 0، وعندها تصبح <code>Rc&lt;List&gt;‎</code> محرَّرة تمامًا. يسمح استخدام <code>Rc&lt;T&gt;‎</code> بأن يكون لقيمة واحدة أكثر من مالك، كما أن عدد المراجع يؤكد أن القيمة لا تزال صالحةً طالما لا يزال هناك مالك.
</p>

<p>
	يسمح <code>Rc&lt;T&gt;‎</code> عن طريق المراجع الثابتة immutable مشاركة البيانات بين أقسام متعددة من البرنامج للقراءة فقط. قد تخرق بعض قوانين الاستعارة -التي ناقشناها سابقًا في مقال <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>- في حلقات المرجع Reference Cycles وتسببها بتسريب الذاكرة Memory Leak في لغة رست Rust إذا سمح <code>Rc&lt;T&gt;‎</code> بوجود عدة مراجع متغيّرة أيضًا؛ إذ قد تسبب الاستعارات المتعددة المتغيّرة لنفس المكان حالة تعارض وتناقض للبيانات data race، إلا أن القدرة على تغيير البيانات مفيدة جدًا. سنناقش في القسم القادم النمط الداخلي المتغيّر interior mutability pattern، إضافةً إلى النوع <code>RefCell&lt;T&gt;‎</code> الذي يمكن استخدامه مع <code>Rc&lt;T&gt;‎</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/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1-%D8%A7%D9%84%D8%B0%D9%83%D9%8A-refcell%E2%80%8E-%D9%88%D9%86%D9%85%D8%B7-%D9%82%D8%A7%D8%A8%D9%84%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%BA%D9%8A%D9%8A%D8%B1-%D8%A7%D9%84%D8%AF%D8%A7%D8%AE%D9%84%D9%8A-interior-mutability-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2037/" rel="">المؤشر الذكي Refcell&lt;T&gt;‎ ونمط قابلية التغيير الداخلي interior mutability في لغة رست</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/rust/%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D8%B9%D9%86%D8%AF-%D8%AA%D8%AD%D8%B1%D9%8A%D8%B1-%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-cleanup-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%B3%D9%85%D8%A9-drop-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2030/" rel="">تنفيذ شيفرة برمجية عند تحرير الذاكرة cleanup باستخدام السمة Drop في لغة رست</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>
	<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%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%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>
</ul>
]]></description><guid isPermaLink="false">2036</guid><pubDate>Tue, 25 Jul 2023 11:00:00 +0000</pubDate></item><item><title>&#x62A;&#x646;&#x641;&#x64A;&#x630; &#x634;&#x64A;&#x641;&#x631;&#x629; &#x628;&#x631;&#x645;&#x62C;&#x64A;&#x629; &#x639;&#x646;&#x62F; &#x62A;&#x62D;&#x631;&#x64A;&#x631; &#x627;&#x644;&#x630;&#x627;&#x643;&#x631;&#x629; cleanup &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x627;&#x644;&#x633;&#x645;&#x629; Drop &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A;</title><link>https://academy.hsoub.com/programming/rust/%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D8%B9%D9%86%D8%AF-%D8%AA%D8%AD%D8%B1%D9%8A%D8%B1-%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-cleanup-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%B3%D9%85%D8%A9-drop-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2030/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_07/------cleanup---Drop---.png.a9c12cb84ce2463a98c7505a31c25bdf.png" /></p>
<p>
	السمة الثانية المهمة لنمط <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="">المؤشرات الذكية</a> smart pointer pattern هي السمة <code>Drop</code>، التي تسمح لك بتخصيص ماذا يحدث إذا كانت القيمة ستخرج من النطاق scope. يمكنك تأمين تنفيذ لسمة <code>Drop</code> على أي نوع، ويمكن استخدام هذه الشيفرة البرمجية لتحرير الموارد، مثل الملفات واتصالات الشبكة.
</p>

<p>
	قدّمنا السمة <code>Drop</code> في سياق المؤشرات الذكية، إذ تُستخدم وظيفة سمة <code>Drop</code> دائمًا عند تطبيق مؤشر ذكي، ومثال على ذلك: عندما يُحرَّر <code>Box&lt;T&gt;‎</code>ستحرّر المساحة المخصصة له في الكومة heap التي يشير إليها الصندوق box.
</p>

<p>
	يتوجب على المبرمج في بعض لغات البرمجة ولبعض الأنواع استدعاء الشيفرة البرمجية التي تحرّر مساحة تخزين أو موارد في كل مرة ينتهي من استخدام نسخة instance من هذه الأنواع، ومن الأمثلة على ذلك مقابض الملفات file handles والمقابس sockets أو الأقفال locks، وإذا نسي المبرمجون استدعاء تلك الشيفرة البرمجية (لتحرير مساحة التخزين والموارد)، سيزداد التحميل على النظام وسيتوقف النظام عن العمل بحلول نقطة معيّنة. يمكنك في لغة رست تحديد قسم معين من الشيفرة تُنفذ عندما تخرج قيمة ما من النطاق، إذ سيضيف المصرف هذه الشيفرة تلقائيًا ونتيجةً ذلك لا تحتاج أن تكون حذرًا بخصوص وضع شيفرة تحرير المساحة البرمجية cleanup code في كل مكان في البرنامج عندما تكون نسخة من نوع معين قد انتهت، أي لن يكون هناك أي هدر في الموارد.
</p>

<p>
	يمكنك تحديد الشيفرة البرمجية التي تريد تنفيذها عندما تخرج قيمة ما عن النطاق وذلك باستخدام تنفيذ سمة <code>Drop</code>، إذ تحتاج سمة <code>Drop</code> أن تطبّق تابع method واحد اسمه <code>drop</code> يأخذ مرجعًا متغيّرًا إلى <code>self</code> لينتظر استدعاء رست للدالة <code>drop</code>. دعنا ننفّذ <code>drop</code> مع تعليمات <code>println!‎</code> في الوقت الحالي.
</p>

<p>
	توضّح الشيفرة 15 البنية <code>CustomSmartPointer</code> بوظيفة مخصّصة وحيدة هي طباعة <code>Dropping CustomSmartPointer!‎</code> عندما تخرج النسخة عن النطاق لإظهار لحظة تنفيذ رست للخاصية <code>drop</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7991_8" style=""><span class="kwd">struct</span><span class="pln"> </span><span class="typ">CustomSmartPointer</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    data</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">Drop</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">CustomSmartPointer</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn drop</span><span class="pun">(&amp;</span><span class="pln">mut self</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">"Dropping CustomSmartPointer with data `{}`!"</span><span class="pun">,</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">data</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 c </span><span class="pun">=</span><span class="pln"> </span><span class="typ">CustomSmartPointer</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        data</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">"my stuff"</span><span class="pun">),</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
    let d </span><span class="pun">=</span><span class="pln"> </span><span class="typ">CustomSmartPointer</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        data</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">"other stuff"</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">"CustomSmartPointers created."</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 15: بنية <code>CustomSmartPointer</code> التي تنفّذ السمة <code>Drop</code> عند مكان وضع شيفرة تحرير الذاكرة]
</p>

<p>
	السمة <code>Drop</code> مضمّنة في المقدمة لذا لا نحتاج لأن نضيفها إلى النطاق. ننفّذ سمة <code>Drop</code> على <code>CustomSmartPointer</code> ونقدّم تنفيذًا لتابع <code>drop</code> الذي يستدعي بدوره <code>println!‎</code>، ونضع في متن دالة <code>drop</code> أي منطق نريد تنفيذه عند تخرج نسخة من النوع خارج النطاق، كما أننا نطبع نصًا هنا لنوضح كيف تستدعي رست <code>drop</code> بصريًا.
</p>

<p>
	أنشأنا في <code>main</code> نسختين من <code>CustomSmartPointer</code> ومن ثم طبعنا <code>CutsomSmartPointers created</code>، سيخرج <code>CustomSmartPointer</code> بنهاية <code>main</code> خارج النطاق، مما يعني أن رست ستستدعي الشيفرة التي وضعناها في تابع <code>drop</code> مما سيتسبب بطباعة رسالتنا النهائية، مع ملاحظة بأننا لم نستدعي التابع <code>drop</code> صراحةً.
</p>

<p>
	نحصل على الخرج التالي عندما ننفذ هذا البرنامج:
</p>

<pre class="ipsCode">$ cargo run
   Compiling drop-example v0.1.0 (file:///projects/drop-example)
    Finished dev [unoptimized + debuginfo] target(s) in 0.60s
     Running `target/debug/drop-example`
CustomSmartPointers created.
Dropping CustomSmartPointer with data `other stuff`!
Dropping CustomSmartPointer with data `my stuff`!
</pre>

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

<h2>
	تحرير قيمة مبكرًا باستخدام دالة std::mem::drop
</h2>

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

<p>
	نحصل على خطأ تصريفي إذا أردنا استدعاء التابع <code>drop</code> الخاص بسمة <code>drop</code> يدويًا وذلك بتعديل دالة <code>main</code> من الشيفرة 15 كما هو موضح:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7991_11" style=""><span class="kwd">struct</span><span class="pln"> </span><span class="typ">CustomSmartPointer</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    data</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">Drop</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">CustomSmartPointer</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn drop</span><span class="pun">(&amp;</span><span class="pln">mut self</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">"Dropping CustomSmartPointer with data `{}`!"</span><span class="pun">,</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">data</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 c </span><span class="pun">=</span><span class="pln"> </span><span class="typ">CustomSmartPointer</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        data</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">"some data"</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">"CustomSmartPointer created."</span><span class="pun">);</span><span class="pln">
    c</span><span class="pun">.</span><span class="pln">drop</span><span class="pun">();</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"CustomSmartPointer dropped before the end of main."</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 15: محاولة استدعاء تابع <code>drop</code> من السمة <code>Drop</code> يدوياً لتحرير الذاكرة المبكر]
</p>

<p>
	نحصل على الخطأ التالي عندما نحاول تصريف الشيفرة البرمجية السابقة:
</p>

<pre class="ipsCode">$ cargo run
   Compiling drop-example v0.1.0 (file:///projects/drop-example)
error[E0040]: explicit use of destructor method
  --&gt; src/main.rs:16:7
   |
16 |     c.drop();
   |     --^^^^--
   |     | |
   |     | explicit destructor calls not allowed
   |     help: consider using `drop` function: `drop(c)`

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

<p>
	تبيّن لنا رسالة الخطأ هذه أنه من غير المسموح لنا استدعاء <code>drop</code> صراحةً. تستخدم رسالة الخطأ المصطلح مُفكّك destructor وهو مصطلح برمجي عام لدالة تنظف نسخة ما؛ والمفكّك هو مصطلح معاكس للباني constructor وهو الذي ينشئ نسخةً ما، ودالة <code>drop</code> في رست هي نوع من أنواع المفكّكات.
</p>

<p>
	لا تسمح لنا رست باستدعاء <code>drop</code> صراحةً لأن رست ستستدعي تلقائيًا التابع <code>drop</code> على القيمة في نهاية الدالة <code>main</code> مما سيسبب خطأ التحرير المزدوج double free لأن رست سيحاول تحرير القيمة ذاتها مرتين.
</p>

<p>
	لا يمكننا تعطيل إدخال <code>drop</code> التلقائي عندما تخرج قيمة ما عن النطاق، ولا يمكننا استدعاء التابع <code>drop</code> صراحةً، لذا نحن بحاجة لإجبار القيمة على أن تُنظف مبكرًا باستخدام الدالة <code>std::mem::drop</code>.
</p>

<p>
	تعمل دالة <code>std::mem::drop</code> بصورةٍ مختلفة عن التابع <code>drop</code> في سمة <code>Drop</code>، إذ نستدعيها بتمرير القيمة التي نريد تحريرها قسريًا مثل وسيط argument. الدالة مضمّنة في البداية لذا يمكننا تعديل الدالة <code>main</code> في الشيفرة 15 بحيث تستدعي الدالة <code>drop</code> كما في الشيفرة 16:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7991_13" style=""><span class="kwd">struct</span><span class="pln"> </span><span class="typ">CustomSmartPointer</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    data</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">Drop</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">CustomSmartPointer</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fn drop</span><span class="pun">(&amp;</span><span class="pln">mut self</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">"Dropping CustomSmartPointer with data `{}`!"</span><span class="pun">,</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">data</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 c </span><span class="pun">=</span><span class="pln"> </span><span class="typ">CustomSmartPointer</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        data</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">"some data"</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">"CustomSmartPointer created."</span><span class="pun">);</span><span class="pln">
    drop</span><span class="pun">(</span><span class="pln">c</span><span class="pun">);</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"CustomSmartPointer dropped before the end of main."</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 16: استدعاء <code>std::mem::drop</code> لتحرير القيمة صراحةً قبل الخروج من النطاق]
</p>

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

<pre class="ipsCode">$ cargo run
   Compiling drop-example v0.1.0 (file:///projects/drop-example)
    Finished dev [unoptimized + debuginfo] target(s) in 0.73s
     Running `target/debug/drop-example`
CustomSmartPointer created.
Dropping CustomSmartPointer with data `some data`!
CustomSmartPointer dropped before the end of main.
</pre>

<p>
	يُطبع النص "Dropping CustomSmartPointer with data <code>some data</code>!‎" بين النصين ".CustomSmartpointer created" و "CustomSmartPointer dropped before the end of main"، ويدلّ ذلك إلى أن شيفرة التابع <code>drop</code> استُدعيت لتحرير <code>c</code> في تلك النقطة.
</p>

<p>
	يمكنك استخدام شيفرة برمجية مخصصة في تطبيق سمة <code>drop</code> بطرق عديدة وذلك بهدف جعل عملية تحرير الذاكرة سهلة ومريحة، إذ يمكنك مثلًا استخدامها لإنشاء مُخَصص ذاكرة memory allocator خاص بك. ولا داعي لتذكر عملية تحرير الذاكرة مع سمة <code>Drop</code> وفي <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> وذلك لأن رست تفعل ذلك تلقائيًا، ولا داعي أيضًا للقلق من المشاكل الناتجة في حال تحرير قيم لا تزال قيد الاستخدام، إذ يضمن نظام الملكية أن المراجع صحيحة وأن <code>drop</code> يُستدعى فقط عندما تكون القيمة غير مستخدمة بعد الآن.
</p>

<p>
	الآن بعد أن رأينا <code>Box&lt;T&gt;‎</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/%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1-rc%E2%80%8E-%D8%A7%D9%84%D8%B0%D9%83%D9%8A-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D9%87-%D9%84%D9%84%D8%A5%D8%B4%D8%A7%D8%B1%D8%A9-%D8%A5%D9%84%D9%89-%D8%B9%D8%AF%D8%AF-%D8%A7%D9%84%D9%85%D8%B1%D8%A7%D8%AC%D8%B9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-rust-r2036/" rel="">المؤشر Rc&lt;T&gt;‎ الذكي واستخدامه للإشارة إلى عدد المراجع في لغة رست Rust</a>
	</li>
	<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 مثل مراجع نمطية Regular References باستخدام سمة Deref في لغة رست</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%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>
	</li>
</ul>
]]></description><guid isPermaLink="false">2030</guid><pubDate>Mon, 17 Jul 2023 16:00:00 +0000</pubDate></item><item><title>&#x645;&#x639;&#x627;&#x645;&#x644;&#x629; &#x627;&#x644;&#x645;&#x624;&#x634;&#x631;&#x627;&#x62A; &#x627;&#x644;&#x630;&#x643;&#x64A;&#x629; Smart Pointers &#x643;&#x645;&#x631;&#x627;&#x62C;&#x639; &#x646;&#x645;&#x637;&#x64A;&#x629; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; Deref &#x641;&#x64A; &#x644;&#x63A;&#x629; &#x631;&#x633;&#x62A;</title><link>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%83%D9%85%D8%B1%D8%A7%D8%AC%D8%B9-%D9%86%D9%85%D8%B7%D9%8A%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-deref-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2029/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_07/---Smart-Pointers---Regular-References---Deref----Rust.png.79cfbe43a6fb342f3b9e9d4b2b045b0a.png" /></p>
<p>
	يسمح لك تطبيق سمة <code>Deref</code> بتخصيص سلوك عامل التحصيل dereference operator <code>*</code> (انتبه عدم الخلط مع عامل عمليات الضرب أو عامل glob) يمكننا عند تطبيق <code>deref</code> بطريقة معينة تسمح بمعاملة المؤشرات الذكية smart pointers مثل مراجع نمطية، كتابة الشيفرة البرمجية بحيث تعمل على المراجع وتُستخدم بالمؤشرات الذكية أيضًا.
</p>

<p>
	لننظر أولًا إلى كيفية عمل عامل التحصيل مع المراجع النمطية regular references، ومن ثم سنحاول تعريف نوع مخصص يتصرف مثل <code>Box&lt;T&gt;‎</code>، وسنرى سبب عدم عمل عامل التحصيل مثل مرجع على النوع المخصص المعرف حديثاً. سنكتشف كيف يسمح تطبيق سمة <code>Deref</code> للمؤشرات الذكية بأن تعمل بطريقة مماثلة للمراجع، ثم سننظر إلى ميزة التحصيل القسري deref coercion في رست وكيف تسمح لنا بالعمل مع المراجع أو المؤشرات الذكية.
</p>

<p>
	<strong>ملاحظة</strong>: هناك فرق كبير بين النوع <code>MyBox&lt;T&gt;‎</code> الذي سننشئه و <code>Box&lt;T&gt;‎</code> الحقيقي: إذ لن يخزن إصدارنا منه البيانات على الكومة heap، وسنركز في مثالنا هذا على السمة <code>deref</code>، فمكان تخزين البيانات ليس مهمًا بقدر أهمية السلوك المشابه لسلوك المؤشر.
</p>

<h2>
	تتبع المؤشر للوصول إلى القيمة
</h2>

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

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5057_8" 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">
    let y </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">

    assert_eq</span><span class="pun">!(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> x</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"> </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;">
	[الشيفرة 6: استخدام عامل التحصيل لتتبع المرجع وصولًا إلى قيمة من النوع <code>i32</code>]
</p>

<p>
	يخزّن المتغير <code>x</code> قيمةً من النوع <code>i32</code> هي 5. ضبطنا قيمة <code>y</code> بحيث تساوي مرجعًا إلى المتغير <code>x</code>، ويمكننا التأكد أن <code>x</code> تساوي 5، ولكن إذا أردنا التأكد من قيمة <code>y</code> علينا استخدام <code>‎*y</code> لتتبع المرجع وصولًا للقيمة التي يدل عليها لتحصيلها (هذا هو السبب في حصول عملية التحصيل على اسمها)، وذلك لكي يستطيع المصرّف أن يقارنها مع القيمة الفعلية. نستطيع الحصول على قيمة العدد الصحيح <code>y</code> بعد تحصيل <code>y</code> وهي القيمة التي تشير على ما يمكن مقارنته مع 5.
</p>

<p>
	نحصل على الخطأ التالي عند التصريف إذا حاولنا كتابة <code>assert_eq!(5,y);‎</code>:
</p>

<pre class="ipsCode">$ cargo run
   Compiling deref-example v0.1.0 (file:///projects/deref-example)
error[E0277]: can't compare `{integer}` with `&amp;{integer}`
 --&gt; src/main.rs:6:5
  |
6 |     assert_eq!(5, y);
  |     ^^^^^^^^^^^^^^^^ no implementation for `{integer} == &amp;{integer}`
  |
  = help: the trait `PartialEq&lt;&amp;{integer}&gt;` is not implemented for `{integer}`
  = help: the following other types implement trait `PartialEq&lt;Rhs&gt;`:
            f32
            f64
            i128
            i16
            i32
            i64
            i8
            isize
          and 6 others
  = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)

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

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

<h2>
	استخدام Box<t>‎ مثل مرجع</t>
</h2>

<p>
	يمكننا إعادة كتابة الشيفرة البرمجية في الشيفرة 6 باسخدام <code>Box&lt;T&gt;‎</code> بدلاً من مرجع، وذلك عن طريق استخدام عامل التحصيل الذي استخدمناه على <code>Box&lt;T&gt;‎</code> كما هو موضح في الشيفرة 7 بطريقة مماثلة لعمل عامل التحصيل المُستخدم في الشيفرة 6:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5057_11" 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">
    let y </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="pln">x</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"> x</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"> </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;">
	[الشيفرة 7: استخدام عامل التحصيل على <code>Box&lt;i32&gt;‎</code>]
</p>

<p>
	الفرق الأساسي بين الشيفرة 7 والشيفرة 6 هو أننا حددنا <code>y</code> هنا لتكون نسخةً instance عن <code>Box&lt;T&gt;‎</code> وتشير إلى قيمة منسوخة من <code>x</code> بدلًا من أن تكون مرجعًا يشير إلى قيمة <code>x</code>، ويمكننا في التوكيد assertion الأخير استخدام عامل التحصيل لتتبُّع مؤشر <code>Box&lt;T&gt;‎</code> بالطريقة ذاتها التي اتبعناها عندما كان المرجع هو <code>y</code>. سنبحث تاليًا عن الشيء الذي يميّز <code>Box&lt;T&gt;‎</code> ليسمح لنا باستخدام عامل التحصيل بتعريف نوع خاص بنا.
</p>

<h2>
	تعريف المؤشر الذكي الخاص بنا
</h2>

<p>
	دعنا نبني <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="">مؤشرًا ذكيًا</a> خاصًا بنا بصورةٍ مماثلة للنوع <code>Box&lt;T&gt;‎</code> الذي تزودنا به المكتبة القياسية لملاحظة كيف أن المؤشرات الذكية تتصرف على نحوٍ مختلف عن المراجع افتراضيًا، وسننظر بعدها إلى كيفية إضافة قدرة استخدام عامل التحصيل.
</p>

<p>
	النوع <code>Box&lt;T&gt;‎</code> معرفٌ مثل هيكل صف tuple struct بعنصر واحد، لذا نعرّف في الشيفرة 8 نوع <code>MyBox&lt;T&gt;‎</code> بالطريقة ذاتها، كما نعرف أيضاً دالة <code>new</code> لتطابق الدالة <code>new</code> المعرفة في <code>Box&lt;T&gt;‎</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5057_13" style=""><span class="kwd">struct</span><span class="pln"> </span><span class="typ">MyBox</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;(</span><span class="pln">T</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">MyBox</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"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">MyBox</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">MyBox</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="pun">}</span><span class="pln">

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

<p style="text-align: center;">
	[الشيفرة 8: تعريف النوع <code>MyBox&lt;T&gt;‎</code>]
</p>

<p>
	نعرف بنية بالاسم <code>MyBox</code> ونعرف معاملًا مُعمّمًا ‎‏generic ‏ <code>‎T</code>‏ لأننا نريد لنوعنا أن يحتفظ بكل أنواع القيم. نوع <code>MyBox</code> هو صف tuple بعنصر واحد من النوع <code>T</code>. تأخذ دالة <code>MyBox::new</code> معاملًا واحدًا من النوع <code>T</code> وتُعيد نسخةً من <code>MyBox</code> تحتفظ بالقيمة المُمرّرة.
</p>

<p>
	لنجرب إضافة دالة <code>main</code> الموجودة في الشيفرة 7 إلى الشيفرة 8 ونعدلّها بحيث تستخدم النوع <code>MyBox&lt;T&gt;‎</code> الذي عرفناه بدلًا من <code>Box&lt;T&gt;‎</code>. لن تُصّرف الشيفرة 9 لأن رست لا تعرف كيفية تحصيل قيمة <code>MyBox</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5057_15" style=""><span class="kwd">struct</span><span class="pln"> </span><span class="typ">MyBox</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;(</span><span class="pln">T</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">MyBox</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"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">MyBox</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">MyBox</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="pun">}</span><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">
    let y </span><span class="pun">=</span><span class="pln"> </span><span class="typ">MyBox</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="pln">x</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"> x</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"> </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;">
	[الشيفرة 9: محاولة استخدام <code>Mybox&lt;T&gt;‎</code> بطريقة استخدام المراجع و<code>Box&lt;T&gt;‎</code>]
</p>

<p>
	إليك الخطأ التصريفي الناتج عن الشيفرة السابقة:
</p>

<pre class="ipsCode">$ cargo run
   Compiling deref-example v0.1.0 (file:///projects/deref-example)
error[E0614]: type `MyBox&lt;{integer}&gt;` cannot be dereferenced
  --&gt; src/main.rs:14:19
   |
14 |     assert_eq!(5, *y);
   |                   ^^

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

<p>
	لا يمكن تحصيل النوع <code>MyBox&lt;T&gt;‎</code> الخاص بنا لأننا لم نطبق هذه الميزة على نوعنا، ولتطبيق التحصيل باستخدام العامل <code>*</code>، نطبّق السمة <code>Deref</code>.
</p>

<h2>
	معاملة النوع مثل مرجع بتطبيق السمة Deref
</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-%D8%B1%D8%B3%D8%AA-rust-r1935/" rel="">سابقًا</a>، إذ تحتاج السمة <code>Deref</code> الموجودة في المكتبة القياسية لتطبيق تابع واحد اسمه <code>deref</code> يستعير <code>self</code> ويعيد مرجعًا للبيانات الداخلية. تحتوي الشيفرة 10 على تطبيق <code>Deref</code> لإضافة تعريف <code>MyBox</code>:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5057_17" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">ops</span><span class="pun">::</span><span class="typ">Deref</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">Deref</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">MyBox</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">
    type </span><span class="typ">Target</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> T</span><span class="pun">;</span><span class="pln">

    fn deref</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="typ">Self</span><span class="pun">::</span><span class="typ">Target</span><span class="pln"> </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="lit">0</span><span class="pln">
    </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">MyBox</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;(</span><span class="pln">T</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">MyBox</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"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">MyBox</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">MyBox</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="pun">}</span><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">
    let y </span><span class="pun">=</span><span class="pln"> </span><span class="typ">MyBox</span><span class="pun">::</span><span class="kwd">new</span><span class="pun">(</span><span class="pln">x</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"> x</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"> </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;">
	[الشيفرة 10: تطبيق <code>Deref</code> على <code>MyBox&lt;T&gt;‎</code>]
</p>

<p>
	نعرّف في السطر <code>type Target = T;‎</code> نوعًا مرتبط associated type لسمة <code>Deref</code> لتستخدمه، وتختلف الأنواع المرتبطة قليلًا في تعريف المعاملات المعمّمة، ولكن لا داعي للقلق بخصوصها حاليَا إذ سنتطرق لهذا الموضوع لاحقًا.
</p>

<p>
	نكتب في متن التابع <code>deref</code> المرجع <code>‎&amp;self.0</code> بحيث يُعيد <code>deref</code> مرجعًا للقيمة التي نريد الوصول إليها باستخدام العامل <code>*</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="">سابقًا</a> أن <code>‎.0</code> تصل إلى القيمة الأولى في هيكل الصف). تستطيع الدالة <code>main</code> في الشيفرة 9 التي تستدعي <code>*</code> على القيمة <code>MyBox&lt;T&gt;‎</code> أن تُصرّف الآن مع نجاح التأكيدات.
</p>

<p>
	يستطيع المصرّف أن يحصّل المراجع <code>&amp;</code> فقط بدون سمة <code>Deref</code>، إذ يعطي تابع <code>deref</code> المصرف القدرة على أن يأخذ القيم من أي نوع يطبق <code>Deref</code> وأن يستدعي التابع <code>deref</code> للحصول على مرجع <code>&amp;</code> يعرف كيفية تحصيله.
</p>

<p>
	عندما أدخلنا <code>‎*y</code> في الشيفرة 9، نفذت رست الشيفرة التالية خلف الكواليس:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5057_19" style=""><span class="pun">*(</span><span class="pln">y</span><span class="pun">.</span><span class="pln">deref</span><span class="pun">())</span></pre>

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

<p>
	يعود السبب في إعادة تابع <code>deref</code> المرجع إلى قيمة وأن التحصيل العادي خارج القوسين في <code>‎*(y.deref())‎</code> لا يزال ضروريًا إلى نظام الملكية؛ فإذا أعاد التابع <code>deref</code> القيمة مباشرة بدلاً من مرجع للقيمة فإن القيمة ستنتقل خارج <code>self</code>، ولا نريد أن نأخذ ملكية القيمة الداخلية في <code>MyBox&lt;T&gt;‎</code> في هذه الحالة وفي معظم الحالات التي نستخدم فيها معامل التحصيل.
</p>

<p>
	لاحظ أن المعامل <code>*</code> يُستبدل باستدعاءٍ للتابع <code>deref</code> ثم استدعاء للمعامل <code>*</code> مرةً واحدةً وذلك في كل مرة نستخدم <code>*</code> في شيفرتنا البرمجية. ينتهي بنا المطاف بقيمة من النوع <code>i32</code> تُطابق "5" في <code>assert_eq!‎</code> في الشيفرة 9 وذلك لأن عملية استبدال المعامل <code>*</code> لا تُنفّذ إلى ما لا نهاية.
</p>

<h2>
	التحصيل القسري الضمني مع الدالات والتوابع
</h2>

<p>
	يحوّل التحصيل القسري المرجع من نوع يطبق السمة <code>Deref</code> إلى مرجع من نوع آخر، فمثلاً يحوّل التحصيل القسري <code>‎&amp;String</code> إلى <code>‎&amp;str</code> لأن <code>String</code> يطبق السمة <code>Deref</code> بطريقة تُعيد <code>‎&amp;str</code>. التحصيل القسري هو عملية ملائمة في رست تُجرى على وسطاء arguments الدوال والتوابع وتعمل فقط على الأنواع التي تطبق السمة <code>Deref</code>، وتحصل هذه العملية تلقائيًا عندما نمرر مرجعًا لقيمة ذات نوع معين مثل وسيط لدالة أو تابع لا يطابق نوع المعامل في تعريف الدالة أو التابع. تحوِّل سلسلةً من الاستدعاءات إلى التابع <code>deref</code> النوع المُقدم إلى نوع يحتاجه المعامل.
</p>

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

<p>
	لمشاهدة عمل التحصيل القسري عمليًا، نستخدم النوع <code>MyBox&lt;T&gt;‎</code> الذي عرفناه في الشيفرة 8 بالإضافة إلى تطبيق <code>Deref</code> الذي أضفناه في الشيفرة 10. توضح الشيفرة 11 تعريف دالة تحتوي على معامل شريحة سلسلة نصية string slice:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5057_21" style=""><span class="pln">fn hello</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">{</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"Hello, {name}!"</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 style="text-align: center;">
	[الشيفرة 11: الدالة <code>hello</code> التي تحتوي على معامل <code>name</code> من النوع <code>‎&amp;str</code>]
</p>

<p>
	بإمكاننا استدعاء الدالة <code>hello</code> باستخدام شريحة سلسلة نصية بمثابة وسيط مثل <code>hello(“Rust”);‎</code>. يجعل التحصيل القسري استدعاء <code>hello</code> مع مرجع للقيمة <code>MyBox&lt;String&gt;‎</code> ممكنًا كما هو موضح في الشيفرة 12:
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5057_23" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">ops</span><span class="pun">::</span><span class="typ">Deref</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">Deref</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">MyBox</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">
    type </span><span class="typ">Target</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> T</span><span class="pun">;</span><span class="pln">

    fn deref</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="lit">0</span><span class="pln">
    </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">MyBox</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;(</span><span class="pln">T</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">MyBox</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"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">MyBox</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">MyBox</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="pun">}</span><span class="pln">

fn hello</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">{</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"Hello, {name}!"</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 m </span><span class="pun">=</span><span class="pln"> </span><span class="typ">MyBox</span><span class="pun">::</span><span class="kwd">new</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">"Rust"</span><span class="pun">));</span><span class="pln">
    hello</span><span class="pun">(&amp;</span><span class="pln">m</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 12: استدعاء <code>hello</code> باستخدام مرجع إلى القيمة <code>MyBox&lt;String&gt;‎</code> ويمكن تنفيذ ذلك بفضل التحصيل القسري]
</p>

<p>
	نستدعي هنا الدالة <code>hello</code> مع الوسيط <code>‎&amp;m</code> الذي يمثل مرجعًا إلى القيمة <code>MyBox&lt;String&gt;‎</code>، وتستطيع رست تحويل <code>‎&amp;MyBox&lt;String&gt;‎</code> إلى <code>‎&amp;String</code> باستدعاء <code>deref</code> وذلك لأننا طبقنا السمة <code>Deref</code> على <code>MyBox&lt;T&gt;‎</code> كما هو موضح في الشيفرة 10. تقّدم المكتبة القياسية تطبيقًا للسمة <code>Deref</code> على النوع <code>String</code> الذي يعيد لنا شريحة سلسلة نصية ويمكنك العثور على هذه التفاصيل في توثيق الواجهة البرمجية الخاصة بالسمة <code>Deref</code>. تستدعي رست التابع <code>deref</code> مجددًا لتحويل <code>‎&amp;String</code> إلى <code>‎&amp;str</code> الذي يطابق تعريف دالة <code>hello</code>.
</p>

<p>
	إذا لم تطبق رست التحصيل القسري فسيتوجب علينا كتابة الشيفرة 13 بدلًا من الشيفرة 12 لاستدعاء <code>hello</code> مع قيمة من النوع <code>‎&amp;MyBox&lt;String&gt;‎</code>.
</p>

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

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5057_25" style=""><span class="pln">use std</span><span class="pun">::</span><span class="pln">ops</span><span class="pun">::</span><span class="typ">Deref</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">Deref</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="typ">MyBox</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">
    type </span><span class="typ">Target</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> T</span><span class="pun">;</span><span class="pln">

    fn deref</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="lit">0</span><span class="pln">
    </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">MyBox</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;(</span><span class="pln">T</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">MyBox</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"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">MyBox</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">MyBox</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="pun">}</span><span class="pln">

fn hello</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">{</span><span class="pln">
    println</span><span class="pun">!(</span><span class="str">"Hello, {name}!"</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 m </span><span class="pun">=</span><span class="pln"> </span><span class="typ">MyBox</span><span class="pun">::</span><span class="kwd">new</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">"Rust"</span><span class="pun">));</span><span class="pln">
    hello</span><span class="pun">(&amp;(*</span><span class="pln">m</span><span class="pun">)[..]);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	[الشيفرة 13: الشيفرة التي يجب علينا كتابتها إذا لم تحتوي رست على ميزة التحصيل القسري]
</p>

<p>
	يحصّل <code>(m*)</code> النوع <code>MyBox&lt;String&gt;‎</code> إلى <code>String</code> ومن ثم إلى <code>&amp;</code> وتأخذ <code>[..]</code> شريحة سلسلة نصية <code>String</code> تساوي قيمة السلسلة النصية كاملةً وذلك لمطابقة بصمة الدالة <code>hello</code>. ستكون هذه الشيفرة البرمجية صعبة القراءة والفهم بدون التحصيل القسري وذلك مع كل الرموز اللازمة، إذ يسمح التحصيل القسري للغة رست بمعالجة هذه التحويلات تلقائياً نيابةً عنّا.
</p>

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

<h2>
	كيفية تعامل التحصيل القسري مع قابلية التغيير
</h2>

<p>
	يمكننا استخدام السمة <code>DerefMut</code> لتجاوز عمل العامل <code>*</code> على المراجع المتغيّرة mutable references بصورةٍ مشابهة لاستخدامنا لسمة <code>Deref</code> لتجاوز عمل العامل <code>*</code> على المراجع الثابتة immutable.
</p>

<p>
	تنفّذ رست عملية التحصيل القسري عندما تجد تطبيقات لأنواع وسمات في ثلاث حالات معيّنة:
</p>

<ul>
	<li>
		من <code>‎&amp;T</code> إلى <code>‎&amp;U</code> عندما <code>T: Deref&lt;Target=U&gt;‎</code>.
	</li>
	<li>
		من <code>‎&amp;mut T</code> إلى <code>‎&amp;mut U</code> عندما <code>T: DerefMut&lt;Target=U&gt;‎</code>.
	</li>
	<li>
		من <code>‎&amp;mut T</code> إلى <code>‎&amp;U</code> عندما <code>T: Deref&lt;Target=U&gt;‎</code>.
	</li>
</ul>

<p>
	الحالتان الأولى والثانية متماثلتان مع فرق أن الثانية هي تطبيق لحالة متغيّرة، بينما تنصّ الحالة الأولى أنه إذا كان لديك <code>‎&amp;T</code> وتطبّق <code>T</code> سمة <code>Deref</code> لنوع ما من <code>U</code>، يمكن الحصول على <code>‎&amp;U</code> بوضوح، والحالة الثانية تشير إلى أن عملية التحصيل القسري ذاتها تحدث للمراجع المتغيّرة.
</p>

<p>
	تعدّ الحالة الثالثة أكثر تعقيدًا؛ إذ تجبر رست تحويل مرجع متغيّر إلى مرجع ثابت، إلا أن العكس غير ممكن، فالمراجع الثابتة لن تُحوّل قسريًا إلى مراجع متغيرة، وذلك بسبب قواعد الاستعارة، وإذا كان لديك مرجعًا متغيّرًا فإن هذا المرجع سيكون المرجع الوحيد لتلك البيانات (وإلا فلن يمكنك تصريف البرنامج). لن يكسرتحويل مرجع متغيّرإلى مرجع ثابت قواعد الاستعارة الافتراضية. تتطلب عملية تحويل المرجع الثابت إلى مرجع متغيّر أن يكون المرجع الثابت الابتدائي هو المرجع الوحيد الثابت للبيانات الخاصة به، إلا أن قوانين الاستعارة لا تضمن لك ذلك، وبالتالي لا يمكن لرست الافتراض بأن تحويل مرجع ثابت إلى مرجع متغيّر هي شيء ممكن.
</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/%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D8%B9%D9%86%D8%AF-%D8%AA%D8%AD%D8%B1%D9%8A%D8%B1-%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-cleanup-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%B3%D9%85%D8%A9-drop-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B1%D8%B3%D8%AA-r2030/" rel="">تنفيذ شيفرة برمجية عند تحرير الذاكرة cleanup باستخدام السمة Drop في لغة رست</a>
	</li>
	<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%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>
	</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%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>
</ul>
]]></description><guid isPermaLink="false">2029</guid><pubDate>Wed, 12 Jul 2023 16:00:00 +0000</pubDate></item></channel></rss>
