اشتباهات رایج گولنگ ‍۱ : ترتیب اجرای defer

از اشتباهات رایج گولنگ که حتما بهش برخورد می کنید و یک مورد به اصطلاح error-prone هست اینه که متوجه ترتیب "اجرای" defer ها داخل یک فانکشن نباشید. (تاکید می کنم ترتیب اجرا چون نحوه evaluation defer هم خودش یک موضوعی هست که بعدا بهش می پردازیم)

داستان چیه!

فرض کنیم تابعی داریم که بیش از یک defer داره:

به نظر شما کدوم defer اول اجرا میشه؟ همه فکر می کنیم که به ترتیب از بالا اجرا میشه اما درست همین جاست که باعث میشه توی یک شرایط خاص به باگ بخورید و ندونید مشکل از کجاست.

داخل یک فانکشن، همیشه آخرین defer اول اجرا میشه. یه عبارتی ترتیب اجرای defer ها توی یک فانکشن به صورت FILO هستند (مانند استک) . بنابراین خروجی ما به این شکل خواهد بود:

// output:
end of the main process
this is the second defer
this is the first defer

کجا مشکل پیش میاد؟

بیاید این موضوع رو روی یک مفهوم کاربردی تست کنیم. فرض کنید یه محصولی دارید که قیمتی داره. برای محاسبه قیمت نهایی شما باید tax رو اضافه کنید به قیمت محصول. از طرفی کسایی که خرید اول انجام میدن ما ۱۰ واحد بهشون تخفیف می خوایم بدیم. پس هم تخفیف داریم و هم مالیات.

نکته اینجاست که محاسبه قیمت نهایی باید این طوری باشه که اول تخفیف کم بشه و بعد مالیات روی پولی که مشتری باید بپردازه اعمال بشه. در غیر این صورت فاکتور معتبر نیست. کد زیر رو ببینید:

خروجی این تابع به صورت زیر هست:

// output:
Total price is  98.1

که محاسبه کاملا درستی هست. قیمت محصول ۱۰۰ دلار بوده اما با تخفیف ۱۰ دلاری ما شده ۹۰ دلار. حالا مالیات این ۹۰ دلار میشه ۸.۱ دلار که اگر جمع بشه خواهیم داشت: ۹۸.۱ دلار

اما اگر خط ۲۰ و ۲۱ رو جا به جا کنیم:

اتقافی که می افته اینه که اول مالیات به کل قیمت اضافه میشه یعنی مالیات ۱۰۰ دلار که میشه ۹ دلار به قیمت اضافه میشه. و بعد تخفیف اعمال میشه که اگر ۱۰ دلار از ۱۰۹ دلار کم کنیم خواهیم داشت ۹۹ دلار. این یعنی قیمت نهایی به اشتباه محاسبه میشه:

// output:
Total price is  99


خلاصه

پس همیشه به این نکته توجه داشته باشید که ترتیب اجرای defer در یک فانکشن به صورت FILO است. و نادیده گرفتن این موضوع می تونه باگ هایی رو به وجود بیاره که پیدا کردنش خیلی سخت باشه.