- كل المقالات
- متى تستعمل subscript آمنًا للمجموعات
متى تستعمل subscript آمنًا للمجموعات
الإضافة تحوّل انهيار الخروج عن الحدود إلى nil. وهذا مفيد حين يأتي الفهرس من خارج شيفرتك، ويخفي خطأً منطقيًّا حين لا يكون كذلك.
الإضافة أربعة أسطر، وتكاد لا تخلو منها شيفرة Swift:
extension Collection {
subscript(safe index: Index) -> Element? {
indices.contains(index) ? self[index] : nil
}
}
let names = ["John", "Mike"]
names[2] // crash
names[safe: 2] // nil
الخروج عن حدود المجموعة من الأشياء القليلة في Swift التي تُنهي العملية، وهذه
الإضافة تحوّله إلى optional يتعامل معه if let.
والمقايضة هي في موضع ظهور الخطأ. فـnames[2] في مصفوفةٍ من عنصرين تعني أنّ
شيفرةً سابقة أنتجت فهرسًا غير موجود. تتوقّف Swift عند ذلك الـsubscript
والفهرسُ الخاطئ في متناولك، وstack trace يبيّن كيف وصل إليه. ومع [safe:]
تحصل على nil، فلا يفعل الفرع شيئًا، ويصير العَرَض الظاهر صفًّا فارغًا أو
مجموعًا ناقصًا بواحد، في موضعٍ آخر ووقتٍ لاحق.
فالسؤال هو: هل تعرف أنّ الفهرس صحيح؟ إن كنت حسبته من count، أو كنت تمرّ على
indices، فأنت تعرف، ويضيف الـoptional فرعًا لحالةٍ لا تقع. وإن جاء الفهرس
من مكانٍ آخر فأنت لا تعرف:
- فهرس مأخوذ من حمولة بيانات أو من رابط
- اختيار حُفظ قبل إعادة التحميل واستُعمل بعدها
- فهرس أعاده callback من إطار عمل لمجموعة عدّلتها بعده
وفي الثلاث جميعًا تأتي المجموعة ويأتي الفهرس من مسارين مختلفين في الشيفرة، وهذا هو ما ينبغي أن تبحث عنه.
وتفصيل في التنفيذ: indices.contains(index) فحص مدًى في Array، حيث indices
هي Range<Int> وcontains بتعقيد O(1). أما في مجموعة لا تدعم الفهرسة
المباشرة فيتحوّل إلى Sequence.contains، الذي يمشي من فهرس البداية — فيكون
someString[safe: i] بتعقيد O(n) بطول النص، خلف شيء يبدو للقارئ subscript.
وحصر الإضافة في RandomAccessCollection يتجنّب ذلك، مقابل فقدانها على
String.
استعملها حيث يكون الفهرس غير موثوق. وفي غير ذلك، التوقّف أنفع من الـoptional.