لغة البرمجة منعرج


تبين أن أول تعليق على المقال الرائع الرؤية الذاتية للغة برمجة مثالية هو إشارة إلى لغة البرمجة Zig . بطبيعة الحال ، أصبح من المثير للاهتمام نوع اللغة التي تدعي أنها مكانة من C ++ و D و Rust. نظرت - اللغة تبدو جميلة ومثيرة للاهتمام إلى حد ما. بناء جملة لطيف يشبه si ، النهج الأصلي لمعالجة الأخطاء ، coroutines المضمنة. هذه المقالة هي لمحة موجزة عن الوثائق الرسمية التي تتخللها أفكارهم وانطباعاتهم من أمثلة التعليمات البرمجية قيد التشغيل.

الابتداء


تثبيت برنامج التحويل البرمجي بسيط للغاية ، بالنسبة لنظام التشغيل Windows ، فقط قم بفك ضغط حزمة التوزيع في بعض المجلدات. نقوم بإنشاء ملف نصي hello.zig في نفس المجلد ، وأدخل الرمز من الوثائق هناك وحفظه. يتم التجميع بواسطة الأمر

zig build-exe hello.zig 

بعد ذلك يظهر hello.exe في نفس الدليل.

بالإضافة إلى التجميع ، يتوفر وضع اختبار الوحدة ، لذلك ، يتم استخدام كتل الاختبار في التعليمات البرمجية ، ويتم تنفيذ التجميع وإطلاق الاختبارات بواسطة الأمر

 zig test hello.zig 

الشذوذ الأول


المحول البرمجي لا يدعم فواصل أسطر Windows (\ r \ n). بطبيعة الحال ، فإن حقيقة أن فواصل الأسطر في كل نظام (وين ، نيكس ، ماك) هي بعض من تلقاء نفسها هي الوحشية وبقايا من الماضي. ولكن لا يوجد شيء يجب القيام به ، لذلك حدد فقط ، على سبيل المثال ، في Notepad ++ التنسيق الذي تريده للمترجم.

الغريب الثاني الذي صادفته عن طريق الصدفة - لا يتم دعم علامات التبويب في الكود! المساحات فقط. لكن هذا يحدث :)

ومع ذلك ، يتم كتابة هذا بصراحة في الوثائق - الحقيقة هي بالفعل في النهاية.

تعليقات


الغريب الآخر هو أن Zig لا يدعم التعليقات متعددة الأسطر. أتذكر أن كل شيء تم بشكل صحيح في توربو باسكال القديمة - تم دعم التعليقات المتداخلة متعددة الأسطر. على ما يبدو ، منذ ذلك الحين لم يتقن أي مطور لغة واحدة مثل هذا الشيء البسيط :)

ولكن هناك تعليقات وثائقية. ابدأ بـ ///. يجب أن يكون في أماكن معينة - أمام الكائنات المقابلة (المتغيرات والوظائف والفئات ...). إذا كانوا في مكان آخر - خطأ ترجمة. ليس سيئا

إعلان متغير


انتهيت من النمط المألوف الآن (والصحيح أيديولوجيًا) ، عند كتابة الكلمة الأساسية (const أو var) أولاً ، ثم الاسم ، ثم اختيارياً الكتابة ، ثم القيمة الأولية. أي نوع الاستنتاج التلقائي هو متاح. يجب تهيئة المتغيرات - إذا لم تحدد قيمة أولية ، فسيحدث خطأ في التحويل البرمجي. ومع ذلك ، يتم توفير قيمة خاصة غير محددة ، والتي يمكن استخدامها صراحة لتحديد المتغيرات غير مهيأة.

 var i:i32 = undefined; 

إخراج وحدة التحكم


بالنسبة للتجارب ، نحتاج إلى الإخراج إلى وحدة التحكم - في جميع الأمثلة ، هذه هي الطريقة المستخدمة. في مجال المكونات الإضافية

 const warn = std.debug.warn; 

والرمز مكتوب مثل هذا:

 warn("{}\n{}\n", false, "hi"); 

يحتوي المحول البرمجي على بعض الأخطاء التي يقوم بإبلاغها بصدق عند محاولة إخراج عدد صحيح أو رقم الفاصلة العائمة بهذه الطريقة:
خطأ: خطأ المحول البرمجي: يجب أن يتم صب حرفي عدد صحيح و تعويم في var args. github.com/ziglang/zig/issues/557

أنواع البيانات


أنواع بدائية


يبدو أن أسماء الأنواع مأخوذة من Rust (i8 ، u8 ، ... i128 ، u128) ، وهناك أيضًا أنواع خاصة للتوافق الثنائي C ، 4 أنواع من أنواع الفاصلة العائمة (f16 ، f32 ، f64 ، f128). هناك نوع منطقي. هناك نوع من الفراغ بطول الصفر وقضاء وقت خاص ، والتي سأناقشها لاحقًا.

يمكنك أيضًا إنشاء أنواع عدد صحيح من أي طول بالبت من 1 إلى 65535. يبدأ اسم النوع بالحرف i أو u ، ثم يتم كتابة الطول بالبت.

 //  ! var j:i65535 = 0x0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF; 

ومع ذلك ، لم أستطع توصيل هذه القيمة إلى وحدة التحكم - حدث خطأ في LLVM أثناء عملية التحويل البرمجي.

بشكل عام ، يعد هذا حلًا مثيرًا للاهتمام ، على الرغم من أنه غامض (IMHO: دعم القيم الحرفية الرقمية الطويلة تمامًا على مستوى المحول البرمجي صحيح ، ولكن أنواع التسمية بهذه الطريقة ليست جيدة جدًا ، من الأفضل القيام بذلك بأمانة من خلال نوع القالب). ولماذا هو الحد 65535؟ لا يبدو أن المكتبات مثل GMP تفرض مثل هذه القيود؟

حرفية السلسلة


هذه صفائف أحرف (بدون زائدة صفر في النهاية). بالنسبة للحرف الحرفية التي تنتهي بصفر ، يتم استخدام البادئة "c".

 const normal_bytes = "hello"; const null_terminated_bytes = c"hello"; 

مثل معظم اللغات ، يدعم Zig تسلسل هروب قياسي وإدراج أحرف Unicode من خلال رموزهم (\ uNNNN ، \ UNNNNNN حيث N رقم سداسي عشري).
يتم تشكيل القيم الحرفية متعددة الأسطر باستخدام خطين مائلين للخلف في بداية كل سطر. لا توجد علامات اقتباس مطلوبة. هذا هو ، بعض المحاولات لإنشاء سلاسل خام ، ولكن IMHO غير ناجح - ميزة السلاسل الأولية هي أنه يمكنك إدراج أي جزء من النص من أي مكان في التعليمات البرمجية - ومن الأفضل عدم تغيير أي شيء ، ولكن هنا يجب عليك إضافة \\ في بداية كل سطر.

 const multiline = \\#include <stdio.h> \\ \\int main(int argc, char **argv) { \\ printf("hello world\n"); \\ return 0; \\} ; 

حرفي صحيح


كل شيء بلغات تشبه si. لقد سررت جدًا لأنه بالنسبة للحرف الثمانية ، يتم استخدام البادئة 0o وليس الصفر فقط ، كما في C. كما يتم دعم القيم الحرفية الثنائية مع بادئة 0b. يمكن أن تكون القيم الفاصلة العائمة ست عشرية (كما هو الحال في ملحق GCC ).

العمليات


بالطبع ، هناك العمليات الحسابية القياسية والمنطقية و bitwise C. يتم دعم العمليات المختصرة (+ = إلخ). بدلاً من && و || الكلمات الرئيسية و أو تستخدم. نقطة مثيرة للاهتمام هو أن العمليات مع دلالات مضمونة مضمونة معتمدة بشكل إضافي. تبدو مثل هذا:

 a +% b a +%= b 

في هذه الحالة ، لا تضمن العمليات الحسابية العادية تجاوز السعة وتعتبر نتائجها أثناء تجاوز السعة غير محددة (ويتم إنشاء أخطاء التجميع للثوابت). IMHO هذا غريب بعض الشيء ، لكن يبدو أنه مصنوع من بعض الاعتبارات العميقة للتوافق مع دلالات اللغة C.

المصفوفات


حرفية الصفيف تبدو كالتالي:

 const msg = []u8{ 'h', 'e', 'l', 'l', 'o' }; const arr = []i32{ 1, 2, 3, 4 }; 

الأوتار صفائف من الأحرف ، كما في C. الفهرسة الكلاسيكية مع الأقواس المربعة. يتم توفير عمليات الجمع (تسلسل) وضرب الصفائف. إنه شيء مثير للاهتمام للغاية ، وإذا كان كل شيء واضحًا مع تسلسل ، ثم الضرب - ظللت أنتظر حتى يقوم شخص ما بتنفيذ هذا ، والآن أنتظر :) في Assembler (!) هناك مثل هذه العملية المزدوجة التي تسمح لك بإنشاء بيانات مكررة. الآن في منعرج:

 const one = []i32{ 1, 2, 3, 4 }; const two = []i32{ 5, 6, 7, 8 }; const c = one ++ two; // { 1,2,3,4,5,6,7,8 } const pattern = "ab" ** 3; // "ababab" 

مؤشرات


بناء الجملة يشبه C.

 var x: i32 = 1234; //  const x_ptr = &x; //   

لإلغاء التسجيل (أخذ القيم حسب المؤشر) ، يتم استخدام عملية postfix غير عادية:

 x_ptr.* == 5678; x_ptr.* += 1; 

يتم تعيين نوع المؤشر بشكل صريح عن طريق تعيين علامة النجمة أمام اسم النوع

 const x_ptr : *i32 = &x; 

شرائح (شرائح)


بنية بيانات مضمنة في اللغة التي تتيح لك الرجوع إلى صفيف أو جزء منه. يحتوي على مؤشر للعنصر الأول وعدد العناصر. يبدو مثل هذا:

 var array = []i32{ 1, 2, 3, 4 }; const slice = array[0..array.len]; 

يبدو أن تؤخذ من الذهاب ، وليس متأكدا. كما أنني لست متأكدًا مما إذا كان الأمر يستحق التضمين بلغة ، في حين أن تنفيذ أي شيء من هذا القبيل بلغة OOP أمر أساسي للغاية.

الهياكل


طريقة مثيرة للاهتمام لإعلان بنية: يتم الإعلان عن ثابت ، يتم عرض نوعه تلقائيًا كـ "type" (type) ، ويتم استخدامه كاسم للهيكل. والهيكل نفسه (هيكل) هو "مجهول".

 const Point = struct { x: f32, y: f32, }; 

من المستحيل تحديد اسم بالطريقة المعتادة باللغات المشابهة لـ C ، ومع ذلك ، يعرض المترجم اسم النوع وفقًا لقواعد معينة - على وجه الخصوص ، في الحالة المذكورة أعلاه ، سيتزامن ذلك مع اسم ثابت "الكتابة".

بشكل عام ، لا تضمن اللغة ترتيب الحقول ومواءمتها في الذاكرة. إذا كانت هناك حاجة إلى ضمانات ، فيجب استخدام الهياكل "المعبأة".

 const Point2 = packed struct { x: f32, y: f32, }; 

التهيئة - بأسلوب تعيين Sishny:

 const p = Point { .x = 0.12, .y = 0.34, }; 

الهياكل قد يكون لها طرق. ومع ذلك ، فإن وضع طريقة في هيكل ما يستخدم ببساطة الهيكل كمساحة اسم ؛ على عكس C ++ ، لا يتم تمرير هذه المعلمات الضمنية.

التحويلات


بشكل عام ، هو نفسه كما في C / C ++. هناك بعض الوسائل المضمنة المريحة للوصول إلى معلومات التعريف ، على سبيل المثال ، عدد الحقول وأسمائها ، التي يتم تنفيذها بواسطة وحدات ماكرو بناء الجملة المضمنة في اللغة (والتي تسمى وظائف مدمجة في الوثائق).

من أجل "التوافق الثنائي مع C" يتم توفير بعض التعدادات الخارجية.

للإشارة إلى النوع الذي يجب أن يكمن وراء التعداد ، بناء النموذج

 packed enum(u8) 

حيث u8 هو النوع الأساسي.
يمكن أن تحتوي التعدادات على طرق مشابهة للبنيات (مثل استخدام اسم التعداد كمساحة اسم).

النقابات


كما أفهمها ، فإن الاتحاد في Zig عبارة عن نوع جبري ، أي يحتوي على حقل علامة مخفي يحدد أي من حقول الاتحاد "نشط". يتم تنفيذ "التنشيط" لحقل آخر عن طريق إعادة التعيين الكامل للرابطة بأكملها. مثال التوثيق

 const assert = @import("std").debug.assert; const mem = @import("std").mem; const Payload = union { Int: i64, Float: f64, Bool: bool, }; test "simple union" { var payload = Payload {.Int = 1234}; // payload.Float = 12.34; // !    assert(payload.Int == 1234); //       payload = Payload {.Float = 12.34}; assert(payload.Float == 12.34); } 

يمكن للنقابات أيضًا استخدام الأرقام بشكل صريح للعلامة.

 // Unions can be given an enum tag type: const ComplexTypeTag = enum { Ok, NotOk }; const ComplexType = union(ComplexTypeTag) { Ok: u8, NotOk: void, }; 

يمكن للنقابات ، مثل التعدادات والهياكل ، توفير مساحة الاسم الخاصة بها للطرق.

أنواع اختياري


منعرج لديه دعم اختياري مدمج. تتم إضافة علامة استفهام قبل اسم النوع:

 const normal_int: i32 = 1234; // normal integer const optional_int: ?i32 = 5678; // optional integer 

ومن المثير للاهتمام ، أن Zig ينفذ شيئًا واحدًا حول الاحتمال الذي اشتبهت فيه ، لكنه لم يكن متأكدًا مما إذا كان صحيحًا أم لا. يتم جعل المؤشرات متوافقة مع الخيارات دون إضافة حقل مخفي إضافي ("علامة") ، والذي يخزن علامة على صحة القيمة ؛ و null يُستخدم كقيمة غير صالحة. وبالتالي ، فإن أنواع المراجع الممثلة في Zig بواسطة مؤشرات لا تتطلب حتى ذاكرة إضافية لـ "الاختيارية". في الوقت نفسه ، يُحظر تعيين القيم الخالية إلى مؤشرات عادية.

أنواع الأخطاء


تشبه الأنواع الاختيارية ، ولكن بدلاً من العلامة المنطقية ("غير صالحة حقًا") ، يتم استخدام عنصر التعداد المقابل لرمز الخطأ. يشبه بناء الجملة الخيارات ، يتم إضافة علامة التعجب بدلاً من علامة الاستفهام. وبالتالي ، يمكن استخدام هذه الأنواع ، على سبيل المثال ، للرجوع من الوظائف: إما أن يتم إرجاع نتيجة الكائن لعملية ناجحة للوظيفة ، أو يتم إرجاع خطأ في الكود المقابل. تعد أنواع الأخطاء جزءًا مهمًا من نظام معالجة الأخطاء بلغة Zig ، لمزيد من التفاصيل ، انظر قسم معالجة الأخطاء.

اكتب الفراغ


متغيرات مثل الفراغ والعمليات معهم ممكنة في منعرج

 var x: void = {}; var y: void = {}; x = y; 

لا يتم إنشاء كود لمثل هذه العمليات ؛ هذا النوع مفيد بشكل رئيسي في metaprogramming.

يوجد أيضًا نوع c_void للتوافق مع C.

مشغلي التحكم والوظائف


وتشمل هذه: الكتل ، التبديل ، بينما ، إذا ، وإلا ، فاصل ، تابع. لتجميع التعليمات البرمجية ، يتم استخدام الأقواس المعقوفة القياسية. فقط كتل ، كما هو الحال في C / C ++ ، وتستخدم للحد من نطاق المتغيرات. يمكن اعتبار الكتل تعبيرات. لا توجد غوتو في اللغة ، ولكن هناك تسميات يمكن استخدامها مع بيانات الاستراحة والمتابعة. بشكل افتراضي ، يعمل هؤلاء المشغلون مع الحلقات ؛ ومع ذلك ، إذا كان للكتلة تسمية ، فيمكنك استخدامها.

 var y: i32 = 123; const x = blk: { y += 1; break :blk y; //   blk   y }; 

يختلف بيان التبديل عن المشغل لأنه لا يحتوي على "سقوط" ، أي يتم تنفيذ شرط واحد فقط (الحالة) ويخرج المفتاح. بناء الجملة أكثر إحكاما: بدلاً من الحالة ، يتم استخدام السهم "=>". يمكن أيضًا اعتبار التبديل تعبيرًا.

في حين أن العبارات هي نفسها كما هو الحال في جميع اللغات تشبه C. للبيان هو أشبه foreach. يمكن اعتبار كل منهم تعبيرات. من الميزات الجديدة ، في حين ولأجل ، وكذلك لو ، يمكن أن يكون لديك كتلة أخرى ينفذ إذا لم يكن هناك تكرار حلقة.

وهنا حان الوقت للحديث عن ميزة شائعة للتبديل ، بينما يتم استعارتها بطريقة أو بأخرى من مفهوم حلقات foreach - "التقاط" المتغيرات. يبدو مثل هذا:

 while (eventuallyNullSequence()) |value| { sum1 += value; } if (opt_arg) |value| { assert(value == 0); } for (items[0..1]) |value| { sum += value; } 

هنا ، تعتبر وسيطة "مصدر" معينة للبيانات ، والتي يمكن أن تكون اختيارية ، بالنسبة لصفيف أو شريحة ، ويحتوي متغير يقع بين خطين عموديين على قيمة "موسعة" - أي ، العنصر الحالي للصفيف أو الشريحة (أو مؤشر إليه) ، والقيمة الداخلية للنوع الاختياري (أو مؤشر إليها).

تأجيل و errdefer البيانات


بيان التنفيذ المؤجل المقترض من Go. إنه يعمل بنفس الطريقة - يتم تنفيذ وسيطة عامل التشغيل هذا عند مغادرة النطاق الذي يتم استخدام المشغل فيه. بالإضافة إلى ذلك ، يتم توفير عامل التشغيل errdefer ، والذي يتم تشغيله إذا تم إرجاع نوع خطأ مع رمز خطأ نشط من الوظيفة. هذا جزء من نظام Zig لمعالجة الأخطاء الأصلي.

مشغل غير قابل للوصول


عنصر برمجة العقد. كلمة أساسية خاصة ، يتم وضعها حيث لا ينبغي أن تأتي الإدارة تحت أي ظرف من الظروف. في حالة وصولها إلى هناك ، يتم إنشاء حالة من الذعر في أوضاع Debug و ReleaseSafe ، وفي ReleaseFast يقوم المحسن بإلقاء هذه الفروع بالكامل.

noreturn


من الناحية الفنية ، إنه نوع متوافق في التعبيرات مع أي نوع آخر. هذا ممكن بسبب حقيقة أن كائن من هذا النوع لن يعود أبدا. نظرًا لأن المشغلين عبارة عن تعبيرات في Zig ، يلزم وجود نوع خاص للتعبيرات التي لن يتم تقييمها أبدًا. يحدث هذا عندما ينقل الجانب الأيمن من التعبير التحكم بشكل لا رجعة فيه إلى مكان خارج. لكسر مثل هذه العبارات ، تابع ، أرجع ، لا يمكن الوصول إليه ، حلقات لانهائية ووظائف لا تُرجع السيطرة أبدًا. للمقارنة ، فإن استدعاء وظيفة عادية (التحكم في العودة) ليس عامل تشغيل ظهرًا ، لأنه على الرغم من نقل التحكم إلى الخارج ، فإنه سيتم إرجاعه إلى نقطة الاتصال عاجلاً أم آجلاً.

وبالتالي ، فإن التعبيرات التالية تصبح ممكنة:

 fn foo(condition: bool, b: u32) void { const a = if (condition) b else return; @panic("do something with a"); } 

يحصل المتغير a على القيمة التي يتم إرجاعها بواسطة عبارة if / else. لهذا الغرض ، يجب أن تُرجع الأجزاء (كلاهما إذا كانت أو غير ذلك) تعبيرًا من نفس النوع. إذا أعاد الجزء منطقيًا ، فسيكون الجزء الآخر هو نوع noreturn ، المتوافق تقنيًا مع أي نوع ، ونتيجة لذلك ، يتم تجميع التعليمات البرمجية دون أخطاء.

وظائف


بناء الجملة كلاسيكي للغات من هذا النوع:

 fn add(a: i8, b: i8) i8 { return a + b; } 

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

ميزة أخرى مثيرة للاهتمام هي أنه في Zig ، لا يمكن تجاهل القيم المعادة إلا صراحة باستخدام الشرطة السفلية _

  _ = foo(); 

هناك انعكاس يسمح لك بالحصول على معلومات متنوعة حول الوظيفة

 const assert = @import("std").debug.assert; test "fn reflection" { assert(@typeOf(assert).ReturnType == void); //    assert(@typeOf(assert).is_var_args == false); //    } 

تنفيذ التعليمات البرمجية في وقت الترجمة


يوفر Zig ميزة قوية - رمز التنفيذ المكتوب في منعرج في وقت الترجمة. لكي يتم تنفيذ التعليمات البرمجية في وقت التحويل البرمجي ، قم فقط بلفها في كتلة باستخدام الكلمة الأساسية وقت التشغيل. يمكن استدعاء نفس الوظيفة في وقت الترجمة وفي وقت التشغيل ، مما يسمح لك بكتابة رمز عالمي. بالطبع ، هناك بعض القيود المرتبطة سياقات مختلفة من التعليمات البرمجية. على سبيل المثال ، في الوثائق في العديد من الأمثلة ، يتم استخدام وقت التحقق من وقت الترجمة:

 // array literal const message = []u8{ 'h', 'e', 'l', 'l', 'o' }; // get the size of an array comptime { assert(message.len == 5); } 

ولكن بطبيعة الحال فإن قوة هذا المشغل أبعد ما يكون عن الكشف الكامل هنا. لذلك ، في وصف اللغة ، يتم تقديم مثال كلاسيكي على الاستخدام الفعال لوحدات الماكرو النحوية - تنفيذ دالة مماثلة لـ printf ، ولكن تحليل سلسلة التنسيق وإجراء جميع أنواع التحقق اللازمة من الوسائط في مرحلة الترجمة.

أيضًا ، يتم استخدام وقت الكلمة للإشارة إلى معلمات وظائف وقت الترجمة ، والتي تشبه وظائف قالب C ++.

    fn max(comptime T: type, a: T, b: T) T { return if (a > b) a else b; } 

خطأ في التعامل


اخترع Zig نظام معالجة الأخطاء الأصلي الذي لم يكن مثل اللغات الأخرى. يمكن أن يسمى هذا "استثناءات صريحة" (في هذه اللغة ، يكون explic شاهد بشكل عام أحد المصطلحات). يبدو أيضًا رموز العودة العودة ، لكنه يعمل بشكل مختلف.

يعتمد نظام معالجة الأخطاء Zig على تعدادات خاصة لتنفيذ رموز الأخطاء المخصصة (خطأ) وبنيت على أساس "أنواع الأخطاء" الخاصة بهم (النوع الجبري-النوع ، والجمع بين نوع الوظيفة التي تم إرجاعها ورمز الخطأ).

يتم الإعلان عن تعداد الأخطاء بنفس طريقة التعدادات العادية:

 const FileOpenError = error { AccessDenied, OutOfMemory, FileNotFound, }; const AllocationError = error { OutOfMemory, }; 

ومع ذلك ، تلقي كافة رموز الخطأ قيم أكبر من الصفر؛ أيضًا ، إذا أعلنت رمزًا يحمل الاسم نفسه في عددين ، فسيحصل على نفس القيمة. ومع ذلك ، فإن التحويلات الضمنية بين الأعداد المختلفة للأخطاء محظورة.

تعني الكلمة الرئيسية anyerror تعدادًا يشمل جميع رموز الأخطاء.

مثل الأنواع الاختيارية ، تدعم اللغة إنشاء أنواع الأخطاء باستخدام بناء جملة خاص. اكتب! U64 عبارة عن نموذج مختصر لأي إرهاب! U64 ، والذي بدوره يعني اتحاد (خيار) ، والذي يتضمن النوع u64 واكتب أي إرهاب (كما أفهمها ، يتم حجز الكود 0 للإشارة إلى عدم وجود خطأ وصلاحية حقل البيانات ، أما باقي الرموز فهي في الواقع رموز الخطأ).

تتيح لك الكلمة الأساسية catch التقاط الخطأ وتحويله إلى قيمة افتراضية:

 const number = parseU64(str, 10) catch 13; 

لذلك ، إذا حدث خطأ في وظيفة parseU64 تُرجع نوع! U64 ، فحينئذٍ سوف يقوم "catch" باعتراضها وإرجاع القيمة الافتراضية وهي 13.

تتيح لك الكلمة الأساسية try "إعادة توجيه" الخطأ إلى المستوى العلوي (أي إلى مستوى وظيفة الاتصال). عرض الكود

 fn doAThing(str: []u8) !void { const number = try parseU64(str, 10); // ... } 

أي ما يعادل هذا:

 fn doAThing(str: []u8) !void { const number = parseU64(str, 10) catch |err| return err; // ... } 

هنا يحدث ما يلي: يتم استدعاء parseU64 ، إذا تم إرجاع خطأ منه - يتم اعتراضه بواسطة عبارة catch ، حيث يتم استخراج رمز الخطأ باستخدام بناء الجملة "الالتقاط" ، والذي تم وضعه في متغير err ، والذي يتم إرجاعه عبر! Void إلى وظيفة الاتصال.

يشير عامل التشغيل Errdefer الموضح سابقًا أيضًا إلى معالجة الأخطاء. يتم تنفيذ رمز وسيطة Errdefer فقط إذا أرجعت الدالة خطأ.

بعض الاحتمالات أكثر. باستخدام || يمكنك دمج مجموعات الأخطاء

 const A = error{ NotDir, PathNotFound, }; const B = error{ OutOfMemory, PathNotFound, }; const C = A || B; 

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

, Zig , C++, Go. , — 4 , ; — . ++, - . — .


Zig . , async, ( , ).

 test "create a coroutine and cancel it" { const p = try async<std.debug.global_allocator> simpleAsyncFn(); comptime assert(@typeOf(p) == promise->void); cancel p; assert(x == 2); } async<*std.mem.Allocator> fn simpleAsyncFn() void { x += 1; } 

async promise->T ( T — ). .

suspend, resume cancel. suspend . suspend, .

resume promise->T , .

cancel .

( ) . :

الصورة

( ) — await. , , , (, ). , :

الصورة


builtin functions — , . , « », . builtin' (sizeOf, tagName, TagType, typeInfo, typeName, typeOf), (import). builtin' C/C++ — , sqrt, popCount, slhExact .. , .

في الختام


. , , . ++ , , , - . Rust , , . D — , , Java, - . Zig — . , .

Source: https://habr.com/ru/post/ar435872/


All Articles