為zig實現(xiàn)接口的億些坑(五)——zig-validate庫上
經(jīng)過之前幾次的嘗逝,在下絞盡腦汁都沒有完成靠譜的接口實現(xiàn),但就在最近網(wǎng)上沖浪的時候,偶然發(fā)現(xiàn)了這個zig-validate這個庫,當(dāng)然對于我們最后要求的那種需要這個庫依舊是做不到的,但其對于一開始的那些設(shè)計的支持優(yōu)于在下的設(shè)計,且是通用解決方案
該庫支持靜態(tài)和動態(tài)兩種方式,其靜態(tài)的方式類似concept(有些許不同,但也夠用),動態(tài)方式與一般的接口無異
由于個人不喜歡傳統(tǒng)接口(注意,rust的trait系統(tǒng)和接口有著本質(zhì)上的區(qū)別,二者的原理大不相同),所以本文將主要以靜態(tài)方式使用該庫,而由于在下很想知道其實現(xiàn)細(xì)節(jié),所以本文還會包含該庫的代碼和實現(xiàn)的分析
那么我們從安裝開始(zig0.11之后編譯和鏈接相關(guān)的api大改,這里在下不得不重新講解一下),首先,安裝依賴的方式有兩種,這里我推薦各位把源碼拉到本地來使用,不要使用官方目前推薦的方式(因為絕大部分的庫在這種使用方式下都或多或少有點bug,目前我知道能用也就只有g(shù)etty)
我們需要在build.zig以module形式添加該庫作為依賴(你說為啥不用包管理器?zigmod和gyro都停更了,而且官方現(xiàn)在有相關(guān)方案了):
這里我將該庫放在了項目根目錄下的libs文件夾,各位可以按照自己的習(xí)慣和喜歡選擇
雖然官方目前的依賴安裝方式很怪且大部分庫都不支持,但這里還是講一下(畢竟以后可能就都轉(zhuǎn)到這種方式了),由于官方并沒有提供相關(guān)的文檔(其實在下的zig相關(guān)知識80%都來自開源社區(qū)和自己踩坑),所以參考了目前在下知道的唯一一個支持這個新方案的庫getty的文檔
我們需要在build.zig中聲明一個dependency:
之后依舊是為你的target添加該module即可
同時,該方式添加的依賴需要在一個名為build.zig.zon(js的對象描述文件叫json,rust的叫ron,那么zon是什么意思應(yīng)該知道了吧)的文件中聲明依賴的相關(guān)信息:
這里的hash值是不能隨便填的,如果不知道可以空著,然后失敗的時候會告訴你expected hash code xxxxxxxxxx,此時把這個值抄上去應(yīng)該問題不大
那么,回歸正題,我們需要將我們之前的兩個接口用該庫重新實現(xiàn),該庫的靜態(tài)使用十分簡單直白,你需要定義一個約束,然后調(diào)用static函數(shù)來驗證某個類型是否滿足該約束,如果驗證通過會返回一個對象,包含Target和Validator兩個成員(目前zig的文檔生成器不能判斷編譯期生成的類型,也不支持自定義,所以如果你打開文檔看,這里應(yīng)該是會看到unknown),即你傳入的兩個類型,static只做了判斷,并沒有對兩個類型做什么修改,但此時返回的target需要額外接收實際類型對象的指針方可運行
那么,我們之前編寫的iterator類型就可以修改為這樣:
而Range類型只需要實現(xiàn)一個next函數(shù)即可,其他的部分可以直接移除,以下測試可以通過
如果使用動態(tài)的方式,由于這里我使用的是迭代器模式,此時會有很多指針,內(nèi)存泄漏的可能性極高,甚至有時候你以為沒有問題的代碼也會崩潰
例如我們以動態(tài)方式實現(xiàn)該接口:
然后,寫一個看似正常的測試:
然后就:

經(jīng)過排查應(yīng)該是問題出在iter解引用這里,但原因就不得而知了(code 1是真的看不出來問題),實際上當(dāng)我將range放到堆上就能解決iter解引用的問題了,但同時之后的調(diào)用又會再一次導(dǎo)致程序崩潰,且同樣是未知的錯誤
經(jīng)過一番回憶(回憶C語言的指針的坑),我找到了解決方案,即直接將*const消除,變?yōu)榭勺兊闹羔?,而不是試圖引入一個新的的可變指針來管理該內(nèi)存
所以,對于zig來說,如果要動態(tài)派發(fā),就會是不是碰到點內(nèi)存問題,尤其是當(dāng)你使用的是可變的指針是
以上是該庫的一點演示,其他的都還不錯,但有一點令人不滿意的地方,就是每次調(diào)用的時候都需要傳入一個指向中間類型的指針,屬實有點麻煩,對于水平一般的程序員(比如我)就可能會在這里處理這個指針的時候把程序跑崩了
那么接下來我們來實現(xiàn)比較的接口,這里我們可以直接把之前的實現(xiàn)方式改成該庫的動態(tài)派發(fā)情況:
然后改代碼觸發(fā)zig的ub,以至于會在無錯誤提示的情況下崩潰
而為了避免內(nèi)存問題和ub,我們需要盡量使用靜態(tài)派發(fā)
這樣就可以正常執(zhí)行
但這樣似乎有點麻煩,所以我們不妨提供一個方便的工具函數(shù)compare來實現(xiàn)一些功能
代碼很簡單,提供兩個默認(rèn)的方式查找是否實現(xiàn)某個trait,以及一個自定義trait的方式
那么此時測試就可以簡化為這樣:
那么,以上是對該庫出于使用者角度的簡單使用
下一章將會對該庫的實現(xiàn)細(xì)節(jié)進(jìn)行講解,看看在下之前究竟菜在哪里