تمرير الأخطاء
جزء من قسم المنطق والتدفق في رحلة Swift على Coddy — الدرس 47 من 56.
يمكن للدالة التي ترمي أخطاء استدعاء دوال أخرى ترمي أخطاء. ينتقل الخطأ تلقائياً إلى أقرب do/catch، دون الحاجة إلى إعادة رمي يدوية.
enum AuthError: Error { case badUser, badPass }
func loadUser(_ name: String) throws -> String {
if name.isEmpty { throw AuthError.badUser }
return name
}
func login(_ name: String, _ pass: String) throws -> String {
let u = try loadUser(name) // يمرر الخطأ
if pass.count < 4 { throw AuthError.badPass }
return "hello \(u)"
}
do {
print(try login("", "abcd"))
} catch AuthError.badUser {
print("missing user")
} catch AuthError.badPass {
print("weak password")
}اقرأ من الأعلى إلى الأسفل: login يستدعي loadUser باستخدام try. إذا أطلق loadUser خطأً، يتخطى الخطأ جسم login ويستقر عند catch الخارجي. أنت تكتب كل خطوة لتفشل بسرعة وتترك للمستدعي قرار ما يجب فعله.
التحدي
متوسطقم ببناء سلسلة معالجة (pipeline) صغيرة تتكون من خطوتين قد تثيران أخطاء (throwing steps).
قم بتعريف StepError: Error مع حالتين: negative، و tooBig.
قم بتعريف parseSafe(_ s: String) throws -> Int: تثير .negative عندما يكون العدد الصحيح الذي تم تحليله أقل من 0، وإلا فإنها تعيده. (افترض أن المدخلات يتم تحليلها بنجاح، فقد تعاملت مع ذلك في مكان آخر.)
قم بتعريف capStrict(_ n: Int) throws -> Int: تثير .tooBig عندما يكون n > 100، وإلا فإنها تعيد n.
قم بتعريف processStep(_ s: String) throws -> Int التي تستدعي parseSafe ثم capStrict، مع السماح لأي خطأ بالانتشار (propagate).
اقرأ سطرًا واحدًا من المدخلات: قائمة من النصوص مفصولة بفاصلة. لكل نص، استدعِ processStep داخل بلوك do/catch واطبع سطرًا واحدًا:
ok: <n>عند النجاحnegativeعندما تثيرparseSafeخطأًtoo bigعندما تثيرcapStrictخطأً
للمدخلات 10,-1,200,42، يكون المخرج هو:
ok: 10
negative
too big
ok: 42ورقة مرجعية
يمكن لـ دالة رامية للأخطاء (throwing function) استدعاء دوال رامية للأخطاء أخرى باستخدام try. تنتقل الأخطاء تلقائيًا إلى أقرب do/catch:
enum AuthError: Error { case badUser, badPass }
func loadUser(_ name: String) throws -> String {
if name.isEmpty { throw AuthError.badUser }
return name
}
func login(_ name: String, _ pass: String) throws -> String {
let u = try loadUser(name) // ينتقل الخطأ للأعلى إذا تم رميه
if pass.count < 4 { throw AuthError.badPass }
return "hello \(u)"
}
do {
print(try login("", "abcd"))
} catch AuthError.badUser {
print("missing user")
} catch AuthError.badPass {
print("weak password")
}إذا قامت loadUser برمي خطأ، يتخطى التنفيذ بقية login وينتقل مباشرة إلى catch الخارجية. لا حاجة لإعادة رمي الخطأ يدويًا.
جرّب بنفسك
let parts = readLine()!.components(separatedBy: ",")
// للقيام به: enum StepError; parseSafe; capStrict; processStep;
// لكل جزء، قم بـ do/try لـ processStep، والتقط (catch) كل فرع، ثم اطبع
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس المنطق والتدفق
1النصوص بعمق
العد والفهارسحالة الأحرف وإزالة الفراغاتالبحث في النصوصتقسيم ودمج النصوصاستبدال النصوص الفرعيةمراجعة - التحقق من اسم المستخدم4المجموعات
إنشاء المجموعاتالاتحاد والتقاطعالطرح والفرق المتماثلالمجموعات الجزئية والشاملةمراجعة - تصفية الـ Tag7مشروع - متتبع العادات
نظرة عامة على المشروعتحديد كمكتمل2النطاقات والخطوات
المغلقة مقابل نصف المفتوحةالخطوة (Stride)دوال النطاقاتالنطاقات في Switchملخص - شبكة التقويم5الدوال من الرتبة العليا - الجزء الأول
MapFilterReduceربط Map و Filter و Reduceمراجعة - إحصائيات الكلمات