آشنایی با دستور Vet در Go — از صفر تا صد

۱۰۶ بازدید
آخرین به‌روزرسانی: ۱۲ مهر ۱۴۰۲
زمان مطالعه: ۴ دقیقه
آشنایی با دستور Vet در Go — از صفر تا صد

دستور Vet در Go در زمان کدنویسی یک کمک بسیار بزرگ محسوب می‌شود. این دستور به تشخیص هر گونه کد مشکوک، غیرمعمول و بی‌استفاده در اپلیکیشن کمک می‌کند. دستور Vet در واقع از چند آنالیز کننده دیگر تشکیل یافته است و حتی می‌تواند با آنالیز کننده سفارشی شما نیز کار کند. در ادامه ابتدا آنالیز کننده داخلی را بررسی می‌کنیم.

آنالیز کننده‌های داخلی

فهرست آنالیز کننده‌های داخلی این زبان در این صفحه (+) موجود است. همچنین با اجرای دستور زیر، می‌توانید آن‌ها را مشاهده کنید:

go tool vet help

ابتدا انواع کمتر بدیهی را بررسی می‌کنیم تا درک بهتری از آن‌ها به دست آوریم.

Atomic

این آنالیز کننده از کاربرد غیرمعمول تابع atomic جلوگیری می‌کند.

1func main() {
2   var a int32 = 0
3
4   var wg sync.WaitGroup
5   for i := 0; i < 500; i++ {
6      wg.Add(1)
7      go func() {
8         a = atomic.AddInt32(&a, 1)
9         wg.Done()
10      }()
11   }
12   wg.Wait()
13}
14main.go:15:4: direct assignment to atomic value

متغیر a به لطف تابع‌های ابتدایی حافظه atomic یعنی addInt که concurrent-safe است، افزایشی است. با این حال نتیجه را به همان متغیر انتساب می‌دهیم که عملیات نوشتن به صورت concurrent-safe نیست. این یک اشتباه از روی بی‌دقتی است که از سوی آنالیز کننده atomic تشخیص داده می‌شود.

Copylocks

چنان که در مستندات توضیح داده شده است، قفل هرگز نباید کپی شود. در واقع این آنالیز کننده به صورت داخلی حالت کنونی قفل را مدیریت می‌کند. به محض این که قفل استفاده شود، یک کپی از این قفل حالت درونی‌اش را کپی می‌کند و به جای ایجاد یک قفل جدید، یک کپی از حالت همان قفل ایجاد می‌شود.

1func main() {
2   var lock sync.Mutex
3
4   l := lock
5   l.Lock()
6   l.Unlock()
7}
8from vet: main.go:9:7: assignment copies lock value to l: sync.Mutex

یک struct که از قفل استفاده می‌کند، باید از سوی اشاره‌گر استفاده شود تا حالت درونی سازگار باقی بماند:

1type Foo struct {
2   lock sync.Mutex
3}
4
5func (f Foo) Lock() {
6   f.lock.Lock()
7}
8
9func main() {
10   f := Foo{lock: sync.Mutex{}}
11   f.Lock()
12}
13from vet: main.go:9:9: Lock passes lock by value: command-line-arguments.Foo contains sync.Mutex

Loopclosure

زمانی که یک goroutine جدید را اجرا می‌کنید، goroutine اصلی همچنان به اجرای خود ادامه می‌دهد. کد goroutine و متغیرهای آن در زمان اجرا ارزیابی خواهند شد و بدین ترتیب می‌تواند منجر به برخی اشتباه‌های رایج شود که در آن متغیر در حالی که هنوز در حال به‌روزرسانی از سوی goroutine اصلی است، مورد استفاده قرار گیرد:

1func main() {
2   var wg sync.WaitGroup
3   for _, v := range []int{0,1,2,3} {
4      wg.Add(1)
5      go func() {
6         print(v)
7         wg.Done()
8      }()
9   }
10   wg.Wait()
11}
123333
13from vet: main.go:10:12: loop variable v captured by func literal

Lostcancel

ایجاد یک context به صورت cancellable از context اصلی موجب بازگشت context جدید به همراه یک تابع می‌شود که می‌تواند این context را لغو کند. این تابع می‌تواند هر زمان برای لغو کردن همه عملیات‌های مرتبط با این context مورد استفاده قرار گیرد، اما باید همواره طوری فراخوانی شود که منجر به نشت هیچ context –ی نشود:

1func Foo(ctx context.Context) {}
2
3func main() {
4   ctx, _ := context.WithCancel(context.Background())
5   Foo(ctx)
6}
7from vet: main.go:8:7: the cancel function returned by context.WithCancel should be called, not discarded, to avoid a context leak

اگر به جزییات بیشتری در مورد context نیاز دارید، مشتقات احتمالی آن و نقش تابع cancel در این مقاله (+) به طور تفصیلی مورد بررسی قرار گرفته است.

Stdmethods

آنالیزکننده stdmethods این اطمینان را حاصل می‌کند که متدهایی که از اینترفیس‌های کتابخانه استاندارد پیاده‌سازی شده‌اند، به خوبی با آن چه انتظار داریم سازگار هستند:

1type Foo struct {}
2
3func (f Foo) MarshalJSON() (string, error) {
4   return `{a: 0}`, nil
5}
6
7func main() {
8   f := Foo{}
9   j, _ := json.Marshal(f)
10   println(string(j))
11}
12{}
13from vet: main.go:7:14: method MarshalJSON() (string, error) should have signature MarshalJSON() ([]byte, error)

Structtag

تگ‌ها رشته‌هایی در struct هستند که باید از قاعده تعریف شده در پکیج reflect (+) پیروی کنند. حتی وجود یک فاصله اضافی موجب می‌شود که تگ معتبر نباشد و بدین ترتیب بدون وجود دستور vet دیباگ کردن آن به کاری دشوار تبدیل می‌شود:

1type Foo struct {
2   A int `json: "foo"`
3}
4
5func main() {
6   f := Foo{}
7   j, _ := json.Marshal(f)
8   println(string(j))
9}
10{"A":0}
11from vet: main.go:6:2: struct field tag `json: "foo"` not compatible with reflect.StructTag.Get: bad syntax for struct tag value

در این بخش برخی از آنالیزکننده‌های دستور Vet را مورد بررسی قرار دادیم، اما این فهرست شامل همه موارد نبود. برای مشاهده فهرست جامعی از این آنالیز کننده‌ها می‌توانید به صفحه این دستور (+) در مستندات زبان Go مراجعه کنید. اما توجه داشته باشید که حتی این فهرست نیز همه قدرت این دستور را نمایش نمی‌دهد. قدرت اصلی این دستور در امکان تعریف کردن آنالیز کننده‌های سفارشی است که در بخش بعدی مورد بررسی قرار می‌دهیم.

آنالیز کننده‌های سفارشی

با این که آنالیزکننده‌های داخلی کاملاً قدرتمند و مفید هستند، اما Go امکان ساخت آنالیز کننده‌های سفارشی را نیز فراهم ساخته است.

برای نمونه می‌توانیم از آنالیزکننده‌های سفارشی برای تشخیص کاربرد پکیج context در آرگومان‌های تابع استفاده کنید. بدین ترتیب زمانی که آنالیز کننده سفارشی آماده شد می‌توانید از آن مستقیماً در دستور Vet استفاده کنید.

go install github.com/blanchonvincent/ctxarg

go vet -vettool=$(which ctxarg)

حتی می‌توان یک ابزار آنالیز سفارشی نیز ساخت.

دستور آنالیز سفارشی

از آنجا که آنالیز کننده‌ها به طور کامل مجزا از دستورها هستند، می‌توان دستور خاصی را به همراه آنالیز کننده مورد نیاز ساخت. در ادامه مثالی از یک دستور سفارشی را می‌بینید که تنها از برخی آنالیزکننده‌های مورد نیاز استفاده می‌کند:

1package main
2
3import (
4	"golang.org/x/tools/go/analysis/multichecker"
5	"golang.org/x/tools/go/analysis/passes/atomic"
6	"golang.org/x/tools/go/analysis/passes/loopclosure"
7	"github.com/blanchonvincent/ctxarg/analysis/passes/ctxarg"
8)
9
10func main() {
11	multichecker.Main(
12		atomic.Analyzer,
13		loopclosure.Analyzer,
14		ctxarg.Analyzer,
15	)
16}

ساخت و اجرای این دستور به ما ابزاری بر مبنای آنالیزکننده‌های منتخب می‌دهد:

./custom-vet help
custom-vet is a tool for static analysis of Go programs.
Registered analyzers:
atomic check for common mistakes using the sync/atomic package
ctxarg check for parameters order while receiving context as parameter
loopclosure check references to loop variables from within nested functions

همچنین می‌توانید مجموعه سفارشی از آنالیز کننده‌ها را ایجاد کرده و آن‌ها را با آنالیز کننده‌های داخلی که دوست دارید ترکیب کنید تا یک دستور سفارشی به دست بیاورید که با گردش کار و استاندارد کدنویسی خاص شما در شرکت‌تان مطابقت دارد.

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۱ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
a-journey-with-go
نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *