处失之道各异。

JavaScript與Python,皆以抛出異常應對;Rust之語言,則變相抛出異常焉。
C 言語與 Go 言語則返一錯誤值,汝必判此值是否為 -1 或空值。
吾常思之,何法为善?
未几,余读旧文。文也,直陈其意抛异常胜于返码其言甚有可取,文似未译中,吾遂译之。
异状与返码
作者:内德·巴切尔德(Ned Batchelder)
原文网址:nedbatchelder.com

于软件,错误之处理有二途:一曰抛出异常,二曰返回性状码。
举世皆以为异常为善,然犹有嗜返回性状码者。是文将释何以异常为优。
一、文洁净
异常可令于大半之文省去错误之处理。其自不摄异常之层,上达于上。由是可撰无错误处理之文,此助于文之简明易读。
今较之,撰同简函数之二法。
先是返回性状码。
STATUS DoSomething(int a, int b) { STATUS st; st = DoThing1(a); if (st != SGOOD) return st; st = DoThing2(b); if (st != SGOOD) return st; return SGOOD; }
于上之例,必判DoThing1(a)DoThing2(b)之返值,果正乎,乃可继之。
若其抛异,则中误之辨,无所用也。
void DoSomething(int a, int b) { DoThing1(a); DoThing2(b); }
此乃至简之境,若逢繁杂数境,则状态码之扰愈甚,而异可存码之洁。
二、有义之返值
状态码占宝贵之返值,必增码以辨其正。
或有函数,本只需返一常值,今必增返误之状。岁月迁流,码量日增,函数渐大,返值亦繁。
譬如,多函数返值有重载:"若败,则返NULL",或败返-1。终致每召此法,必检返值是否为NULL或-1。若函数后增新误返值,则必更诸调用之点。
若抛出异乎寻常,则函数必待事成而后返,诸般谬误之理亦可统于一处而简之。
三、谬误之讯愈丰
状码多为一整数,所载之讯甚寡。设谬误在於不得其文,然何文之失?状码岂能传此众讯?
返状码时,宜录一谬语,置於专司谬记之册,调用者可自其中得详。
异乎寻常者迥异,乃类之实例,故可载众讯。由於异可被衍,异者各异,所载之讯亦异,遂成丰赡之谬语体系。
四、可应隐显之码
有函数不能返状码。如造句之工,无显之返,故不能返状码。更有函数(如解构之工)竟不能直唤,遑论返值乎?
此等无返值之函数,若不施以异常之理,则必另觅之法以示其谬,或佯称此等函数无败之虞。简者或可无咎,然码量日增,败象亦随之。若无以表其败,系统必愈易谬,愈难测也。
五、谬误之显
试思之,若程师怠忽,未书谬误之理,当何如?
若返值之码未加检视,谬误便不得见,码行似成,若事果成。码后或败,然此或乃多步之果,汝将何以溯其谬之始?
然则,若异常不立时捕获,则将沿调用栈上溯,或至更高之 catch 块,或至顶层,委诸操作系统处置,操作系统往往将错误呈示于用户。此于程序非善,然错误至少可见。汝将见异常,能辨其抛出之位,及其应捕获之位,从而修代码。
此不议错误未报之状,此状无论返状态码抑抛异常,皆无益。
故,对于报出之错误未获处理,可归为二端:一者,返状态码隐问题;二者,抛异常使错误可见。汝将择何?
六、辩
著名程序员 Joel Spolsky 谓,返状态码为善,以其谓异常乃更劣之 goto 语句也。
"异常于源码之中,不可得见。阅其文段,莫知何异将发,及其所发之由。是故,纵细察源码,亦难察潜藏之谬。"
"异常使函数生诸多出口。欲撰正码,必思每途之可能。每呼或能发异之函数,若不即捕其异,则函数或骤止,或现他途,非所预也。"
"此言似有理,然若易以返码,则必显察函数每一返点。故,汝以显复易隐复。此亦有利,显复使人只见木而失林,码因之乱。"
"当临此显复,程序员或感烦厌,终或以自法隐其错,或竟略其错。"
前者隐匿谬误之理,惟将显处更易隐处,且不若旧日Try之法便当周全。后者更略谬误之理,尤甚,程序员妄谓某谬不生,遂埋祸根。
七、总论
返值之码甚难用,有处竟不可用。其劫持返值。程序员易不书谬误之理,遂致系统内无声之弊。
异例胜于返值之码。苟编程之语具异例之理,请用之。
(毕)


























