ناممکن اطوار کے بچاؤ

اکثر زیادہ لمبے لائنوں کا استعمال نہیں کرنا چاہئے جو قارئین کو افقی طور پر اسکرول کرنے یا اسکرول کرنے کی زیادہ مقدار میں مجبور کرے۔

۹۹ حرف کی حد تک لائن لمبائی محدود کرنے کا توصیہ دیا گیا ہے۔ مصنفوں کے لئے یہ زیادہ سخت قاعدہ نہیں ہے۔ کوڈ کے لئے اس حد کے پہلے لائن کو توڑنا مستحسن ہے لیکن یہ ایک سخت قاعدہ نہیں ہے۔

مستقل پائیداری

اس دستاویز میں دی گئی اسٹینڈرڈز میں سے کچھ موضوعی فیصلوں، سیٹویشنز یا سیاق و سباق پر مبنی ہیں۔ تاہم، سب سے اہم بات مستقل پائیداری کی ہے۔

مستقل کوڈ کی صیانت کرنا مشکلات کو کم کرتا ہے، منطقی ہوتا ہے، کم سیکھنے کی قیمت ہے، اور نئے کنونشنز ظاہر ہونے یا خرابیاں پیدا ہونے پر مہیا، اپ ڈیٹ اور غلطیوں کو درست کرنا آسان ہوتا ہے۔

برعکس، ایک کوڈ بیس میں مختلف یا اُلٹ کوڈ انداز کو شامل کرنا، صرف صیانتی لاگتوں کی افزائش، غیر یقینیت اور عقلی پچھماۓ کا سبب بنتا ہے۔ یہ سب براہ راست اسپیڈ کو سستا کرتے ہیں، تکلیف دہ کوڈ ریویوز کرپشن کا باعث ہوتے ہیں، اور بگز کی تعداد میں اضافے کا باعث بنتے ہیں۔

ان اسٹینڈرڈز کو کوڈ بیس پر لاگو کرتے ہوئے، پیکج (یا اِس سے بڑے حد تک) کے سطح پر تبدیلیاں کرنا مشورہ دیا جاتا ہے۔ ذیلی پیکج کے سطح پر مختلف سٹائلز کا اطلاق اوپردی چیٹنز کے شامل ہونے والے تشویشات کا مخالف ہوتا ہے۔

مماثل دائرے کو سمویئے میں گروپ کریں

گی جی موہری نسل کے مماثل سٹیٹمنٹس کے گروپ کرنا ممکن ہے۔

تجویز نہیں کی جاتی ہے:

import "a"
import "b"

موصول ہے:

import (
  "a"
  "b"
)

یہ بھی استعمال کیا جاتا ہے مستقل، متغیر اور قسم کے بیانات:

تجویز نہیں کی جاتی ہے:

const a = 1
const b = 2

var a = 1
var b = 2

type Area float64
type Volume float64

موصول ہے:

const (
  a = 1
  b = 2
)

var (
  a = 1
  b = 2
)

type (
  Area float64
  Volume float64
)

صرف مشابہ بیانات کو اکٹھے کریں اور بے تعلق بیانات کو گروپ بندی سے بچائیں۔

تجویز نہیں کی جاتی ہے:

type Operation int

const (
  Add Operation = iota + 1
  Subtract
  Multiply
  EnvVar = "MY_ENV"
)

موصول ہے:

type Operation int

const (
  Add Operation = iota + 1
  Subtract
  Multiply
)

const EnvVar = "MY_ENV"

گروپنگ کہاں استعمال کرنے کی کوئی حد بندی نہیں ہے۔ مثال کے طور پر، آپ یہاں اندر ہی ان استعمال کر سکتے ہیں:

تجویز نہیں کی جاتی ہے:

func f() string {
  red := color.New(0xff0000)
  green := color.New(0x00ff00)
  blue := color.New(0x0000ff)

  ...
}

موصول ہے:

func f() string {
  var (
    red   = color.New(0xff0000)
    green = color.New(0x00ff00)
    blue  = color.New(0x0000ff)
  )

  ...
}

استثناء: اگر متغیر بیانات دیگر متغیرات کے قریب ہیں، خاص طور پر کچھ وقتی فرضی بیانات کے اندر کچھ ہونے کی صورت میں، انہیں اکٹھا کر دیا جائے۔ یہ بِلاولاد متغیرات کے لئے بھی کریں جنہیں ایک ساتھ دیے گئے ہیں.

تجویز نہیں کی جاتی ہے:

func (c *client) request() {
  caller := c.name
  format := "json"
  timeout := 5*time.Second
  var err error
  // ...
}

موصول ہے:

func (c *client) request() {
  var (
    caller  = c.name
    format  = "json"
    timeout = 5*time.Second
    err error
  )
  // ...
}

درآمد گروپنگ

اندازہ کردہ ہے کہ درآمدیں دو قسموں میں گروپنگ ہونی چاہیں:

  • اسٹینڈرڈ لائبریری
  • دیگر لائبریریز

عام طور پر یہ وہ گروپنگ ہے جو توازن کرتا ہے، جو گو آمپورٹس نے لاگو کر دیا ہے۔ تجویز نہیں کی جاتی ہے:

import (
  "fmt"
  "os"
  "go.uber.org/atomic"
  "golang.org/x/sync/errgroup"
)

موصول ہے:

import (
  "fmt"
  "os"

  "go.uber.org/atomic"
  "golang.org/x/sync/errgroup"
)```

## پیکج نام

پیکج کا نام رکھتے وقت، براہ کرم مندرجہ ذیل اصولوں کا پیروی کریں:

- سب کوچک حروف میں، کوئی بڑا حرف یا اندرز نہیں۔
- اکثر معاملات میں، درآمد ہو Our suggestion: کسی ضرورت کے حالات میں نہ ہونا۔
- چھوٹا اور مختصر۔ براہ کرم یاد رکھیں کہ جہاں بھی استعمال ہوتا ہے وہاں نام کی مکمل معلومات ہوتی ہے۔
- مضربات سے بچیں۔ مثال کے طور پر، استعمال کریں `net/url` بجائے کہ `net/urls`۔
- "مشترکہ"، "یوٹل"، "شیئرڈ" یا "لب" کا استعمال نہ کریں۔ یہ کافی معلومات فراہم نہیں کرتے۔
## توقیت کردن توابع


ہم گولانگ کالنٹی کے مطابق [توابع کے ناموں کے لئے مخلوط حروف](https://golang.org/doc/effective_go.html#mixed-caps) کا استعمال کرتے ہیں۔ ایک استثناء تصرف تصور کیا جاتا ہے جب متعلقہ ٹیسٹ کیسز کو گروپ کرنا ہو، جہاں تابع کا نام نیچے خُر سمیت انڈر اسکورز بھی شامل ہو سکتا ہے، جیسے: `TestMyFunction_WhatIsBeingTested`۔


## درآمد کے علیحدہ نام


اگر پیکیج کا نام درآمد راستے کے آخری عنصر سے مشابہ نہ ہو، تو ایک درآمد علامت کا استعمال ضروری ہے۔
```go
import (
  "net/http"

  client "example.com/client-go"
  trace "example.com/trace/v2"
)

دوسرے مقامات میں، درآمد علامات کا استعمال ہونا چاہئے مگر جب بھی درآمد پر لڑائی کا مستقیم تنازع ہو۔ تجویز نہیں کیا گیا:

import (
  "fmt"
  "os"

  nettrace "golang.net/x/trace"
)

تجویز کیا گیا ہے:

import (
  "fmt"
  "os"
  "runtime/trace"

  nettrace "golang.net/x/trace"
)

توابع کا گروپ بندی اور ترتیب

  • توابع بشمولہ ترتیب میں داخل ہونے کی تقریبی ترتیب کے مطابق چھانٹے جانا چاہئے۔
  • ایک ہی فائل میں توابع ریسیور کی مطابقت سے گروپ کر دئیے جانا چاہئے۔

اس لئے، ناجانے توابع فائل میں پہلے آنا چاہئے، جو کہ struct، const، اور var کی تعریف کے بعد آتے ہیں۔

newXYZ()/NewXYZ() کو اس کے بعد بھی دکھایا جا سکتا ہے بائی تعریفوں کے لئے مگر ریسیور کے باقی کے توابع سے پہلے۔

توابع کو ریسیور کی مطابقت سے گروپ کئے گئے ہوتے ہیں، عام خدمت کار فنکشنز فائل کے آخر میں آنے چاہئے۔ تجویز نہیں کیا گیا:

func (s *something) Cost() {
  return calcCost(s.weights)
}

type something struct{ ... }

func calcCost(n []int) int {...}

func (s *something) Stop() {...}

func newSomething() *something {
    return &something{}
}

تجویز کیا گیا ہے:

type something struct{ ... }

func newSomething() *something {
    return &something{}
}

func (s *something) Cost() {
  return calcCost(s.weights)
}

func (s *something) Stop() {...}

func calcCost(n []int) int {...}

گھوم پسری کم کرنا

کوڈ کو گھوم پسری کم کرنا ہوتا ہے جب غلطی/خصوصی مقامات کو جلد سے جلد سامنے لایا جاتا ہے اور یا تو واپسی کیا ہوتا ہے یا لوپ جاری رکھا جاتا ہے۔ گھوم پسری کم کرنے سے متعدد سطوح پر کوڈ کی مقدار کم ہوتی ہے۔

تجویز نہیں کیا گیا:

for _, v := range data {
  if v.F1 == 1 {
    v = process(v)
    if err := v.Call(); err == nil {
      v.Send()
    } else {
      return err
    }
  } else {
    log.Printf("Invalid v: %v", v)
  }
}

تجویز کیا گیا ہے:

for _, v := range data {
  if v.F1 != 1 {
    log.Printf("Invalid v: %v", v)
    continue
  }

  v = process(v)
  if err := v.Call(); err != nil {
    return err
  }
  v.Send()
}

بے ضروری الس

اگر کسی برانچ کے دونوں حصوں میں ایک متغیر کو سیٹ کیا جا رہا ہو، تو اسے ایک ہی اگر بیان کی جگہ دیا جا سکتا ہے۔

تجویز نہیں کیا گیا:

var a int
if b {
  a = 100
} else {
  a = 10
}

تجویز کیا گیا ہے:

a := 10
if b {
  a = 100
}

اعلی سطح پر متغیر کی تنظیم کرنا

اعلی سطح پر، عام var کا استعمال کریں۔ اگر ایک ہی تحریر کی قسم تھیک ہو تو، تائپ کی اصلاح نہ کریں۔

تجویز نہیں کیا گیا:

var _s string = F()

func F() string { return "A" }

تجویز کیا گیا ہے:

var _s = F()
// کیونکہ F نے صریحاً ایک رشتہ قبول کیا ہے، ہمارے لئے _s کی قسم کو صریحاً تفصیل کرنے کی ضرورت نہیں ہے

func F() string { return "A" }

اگر ہمیں اصلاح کرنے کی ضرورت ہو تو تحریر کی قسم واضح کریں۔

type myError struct{}

func (myError) Error() string { return "error" }

func F() myError { return myError{} }

var _e error = F()
// F کو myError کی صورت میں واپسی ملتی ہے، مگر ہمیں error کی ضرورت ہے

غیر انتقال پذیر اعلی سطح کے vars اور consts کے لیے زیر خط کا استعمال کریں

غیر انتقال پذیر اعلی سطح کے vars اور consts کے لیے انہیں وضاحت سے ان کی عالمی فطرت کو ظاہر کرنے کے لیے underscore _ سے شروع کریں جب استعمال ہوں۔

بنیادی منطق: اعلی سطح کے متغیرات اور ثابت پیکیج سطحی رسائی رکھتے ہیں۔ عام نام استعمال کرنے سے آسانی سے دوسرے فائلوں میں غلط قیمت کا بی کنی میں لنکا پڑنے کا خطرہ ہوتا ہے۔

تجویز نہیں کیا گیا:

// foo.go

const (
  defaultPort = 8080
  defaultUser = "user"
)

// bar.go

func Bar() {
  defaultPort := 9090
  ...
  fmt.Println("Default port", defaultPort)

  // اگر Bar() کی پہلی لائن حذف کر دی جائے تو ہمیں کمپیل خرابی نہیں نظر آئے گی۔
}

تجویز کیا گیا ہے:

// foo.go

const (
  _defaultPort = 8080
  _defaultUser = "user"
)

استثناء: غیر انتقال پذیر خرابی کی قیمتیں err prefix بغیر underscore کے استعمال کر سکتی ہیں۔ خرابی کی نام کو دیکھیں۔

nil ایک درست سلائس ہے

nil ایک درست سلائس ہے جس کی لمبائی صفر ہے، جو یہ مطلب ہوتا ہے:

  • آپ کو صریح طور پر لمبائی صفر والی ایک سلائس واپس نہیں کرنا چاہئیں۔ بلکہ 'nil' واپس کریں۔

تجویز نہیں:

if x == "" {
  return []int{}
}

تجویز کیا جاتا ہے:

if x == "" {
  return nil
}
  • ایک سلائس کو خالی ہونے کا پتا لگانے کے لیے ہمیشہ len(s) == 0 استعمال کریں، بجائے کہ nil۔

تجویز نہیں:

func isEmpty(s []string) bool {
  return s == nil
}

تجویز کیا جاتا ہے:

func isEmpty(s []string) bool {
  return len(s) == 0
}
  • صفر قیمت سلائس (سلائس جو var کے ساتھ اعلان کی گئی ہیں) کو فوراً استعمال کیا جا سکتا ہے بغیر make() کو بلانے کے۔

تجویز نہیں:

nums := []int{}
// یا, nums := make([]int)

if add1 {
  nums = append(nums, 1)
}

if add2 {
  nums = append(nums, 2)
}

تجویز کیا جاتا ہے:

var nums []int

if add1 {
  nums = append(nums, 1)
}

if add2 {
  nums = append(nums, 2)
}

یاد رہے، اگرچہ ایک nil سلائس درست سلائس ہے، لیکن یہ ایک صفر کی لمبائی والی سلائس کے برابر نہیں ہے (ایک nil ہے اور دوسرا نہیں)، اور مختلف مواقع میں مختلف طریقوں سے ان کا سلوک ہو سکتا ہے (مثلاً، ترتیب دینا)۔

متغیر کی دائرہ کار کم کریں

اگر ممکن ہو تو متغیر کی دائرہ کار کو کم کرنے کی کوشش کریں، چنانچہ کہ آپ کم گنبد کا قاعدہ کے ساتھ تضاد نہ کریں۔

تجویز نہیں:

err := os.WriteFile(name, data, 0644)
if err != nil {
 return err
}

تجویز کیا جاتا ہے:

if err := os.WriteFile(name, data, 0644); err != nil {
 return err
}

اگر if جملے کے باہر فنکشن کال کا نتیجہ استعمال کیا جانا ہو تو دائرہ کار کم کرنے کی کوشش نہیں کرنی چاہئیے۔

تجویز نہیں:

if data, err := os.ReadFile(name); err == nil {
  err = cfg.Decode(data)
  if err != nil {
    return err
  }

  fmt.Println(cfg)
  return nil
} else {
  return err
}

تجویز کیا جاتا ہے:

data, err := os.ReadFile(name)
if err != nil {
   return err
}

if err := cfg.Decode(data); err != nil {
  return err
}

fmt.Println(cfg)
return nil

ننگا پیرامیٹر استعمال نہ کریں

فنکشن کالز میں غیر واضح پیرامیٹرز پڑھائی کے ساتھ پڑھنے کی قابلیت کو نقصان پہنچا سکتی ہے۔ جب پیرامیٹر ناموں کا مطلب واضح نہ ہو تو، C- اسٹائل کے کمنٹس (/* ... */) پیرامیٹرز کو شامل کریں۔

تجویز نہیں:

// func printInfo(name string, isLocal, done bool)

printInfo("foo", true, true)

تجویز کیا جاتا ہے:

// func printInfo(name string, isLocal, done bool)

printInfo("foo", true /* isLocal */, true /* done */)

اوپر دی گئی مثال کے لئے، اچھا حل یہ ہو سکتا ہے کہ بول نوع کے تمغانے، بنیادی نوعوں کے بجائے، استعمال کیے جائیں۔ اس طریقے سے، پیرامیٹر مستقبل میں دو اشیاء (ٹھیک/غلط) کے علاوہ بھی سپورٹ کر سکتے ہیں۔

type Region int

const (
  UnknownRegion Region = iota
  Local
)

type Status int

const (
  StatusReady Status= iota + 1
  StatusDone
  // شاید ہم مستقبل میں StatusInProgress رکھیں۔
)

func printInfo(name string, region Region, status Status)

Escaping سے بچنے کے لئے راو رشتے کا استعمال کریں

Go، راو رشتے کے لئے کا استعمال کو سپورٹ کرتا ہے، جو کہ " ` " کی نشاندہی کرتا ہے کہ راستہ کا اظہار کرنے والے راستے ہیں۔ جہاں escaping کی ضرورت ہوتی ہے، ہمیں ذاتی طور پر escape کردہ رشتوں کو پڑھنے کی بجائے اس طریقے کا استعمال کرنا چاہئے کہ یہی نشاندہی کیا جا سکتا ہے کہ راستہ کو کیسے پڑھا جائے۔

یہ محفوظ کر سکتا ہے کہ زیادہ پڑھنے کی بجائے راستے خوانی میں سہلیت ہو۔

تجویز نہیں:

wantError := "unknown name:\"test\""

تجویز کیا جاتا ہے:

wantError := `unknown error:"test"`

سٹرکٹس کی ابتدائی کریں

فیلڈ ناموں کا استعمال کر کے سڑکچر کو ابتدائ کریں

ایک سڑکچر کو ابتدائ کرتے وقت، فیلڈ کے ناموں کو تقریباً ہمیشہ ذکر کرنا چاہئے. یہ کمیتی go vet کی طرف سے فوراً لاگو کیا جاتا ہے۔

تجویز نہیں کی جاتی ہے:

k := User{"John", "Doe", true}

تجویز کی جاتی ہے:

k := User{
    FirstName: "John",
    LastName: "Doe",
    Admin: true,
}

استثناء: جب تین یا اس سے کم فیلڈ ہوں، ٹیسٹ ٹیبلز میں فیلڈ ناموں کو شاید غیر ملکوتی کیا جا سکتا ہے۔

tests := []struct{
  op Operation
  want string
}{
  {Add, "add"},
  {Subtract, "subtract"},
}

سڑکچر میں صفر قیمت والے فیلڈ ناموں کا بے حیائی سے استعمال کریں

سڑکچر کو نامی فیلڈوں کے ساتھ ابتدائ کرتے وقت، اگر معنی خیز سیاق وساق فراہم نہیں کی جا رہی ہے، تو صفر قیمت والے فیلڈز کو نظرانداز کریں۔ یعنی، ہمیں خود بخود انہیں صفر قیمتوں پر تنظیم کرنے دیں۔

تجویز نہیں کی جاتی ہے:

user := User{
  FirstName: "John",
  LastName: "Doe",
  MiddleName: "",
  Admin: false,
}

تجویز کی جاتی ہے:

user := User{
  FirstName: "John",
  LastName: "Doe",
}

یہ معاصرت ہمیں ہمیں سائیروں میں پہلی دفعے قیمتیں آنکھوں کے سامنے لانے سے روکتا ہے۔ صرف معنی خیز قیمتوں کو ذکر کریں۔

صرف فیلڈ نامیں مقدم زراعت میں معنوی سیاق وساق فراہم کرنے والے ہوں جہاں سے بھی ممکن ہو، یہاں تک کہ تیار کرنے کے بعد ہدایت کریں۔

tests := []struct{
  give string
  want int
}{
  {give: "0", want: 0},
  // ...
}

صفر قیمتی سڑکچرز کے لئے var استعمال کریں

اگر کسی سڑکچر کے تمام فیلڈ غیر موجود ہوں تو var کا استعمال کریں تاکہ سڑکچر کو دعا کیا جا سکے۔

تجویز نہیں کی جاتی ہے:

user := User{}

تجویز کی جاتی ہے:

var user User

یہ صفر قیمتی سڑکچرز کو نامی قیمتی فیلڈوں والوں سے ممیز کرتا ہے، وہ بھی جیسا کہ ہم خالی سلائس کا اعلان کرتے ہیں.

سڑکچر حوالے کو ابتدائ کریں

جب سڑکچر حوالے کو ابتدائ کیا جائے، سڑکچر کو &T{} کی بجائے new(T) کا استعمال کریں تاکہ یہ سڑکچر کو ابتدائ کرنے میں متمرکز ہو سکے۔

تجویز نہیں کی جاتی ہے:

sval := T{Name: "foo"}

// inconsistent
sptr := new(T)
sptr.Name = "bar"

تجویز کی جاتی ہے:

sval := T{Name: "foo"}

sptr := &T{Name: "bar"}

Maps کو ابتدائ کریں

خالی نقشے کے لئے، اسے make(..) سے ابتدائ کریں اور نقشے کو پروگراماتی کطر سے بھرنے دیں۔ یہ نقشے کی ابتدائ کو دکھنے میں مختلف بناتا ہے، اور یہ بھی آسانی سے اضافے کرنے کے بعد سائز کی ہنڟری فراہم کرتا ہے۔

تجویز نہیں کی جاتی ہے:

var (
  // m1 is read-write safe;
  // m2 panics when writing
  m1 = map[T1]T2{}
  m2 map[T1]T2
)

تجویز کی جاتی ہے:

var (
  // m1 is read-write safe;
  // m2 panics when writing
  m1 = make(map[T1]T2)
  m2 map[T1]T2
)

| اعلان اور ابتدائ دیکھنے میں بہت مشابہ ہے۔ | اعلان اور ابتدائ میں بہت مختلف ہے۔ |

جہاں ممکن ہو، ابتدائ کرنے کے دوران نقشے کی سائز کی تجویز دیں، تفصیلات کے لیے مکمل اضافی کی تجویز دیں۔

علاوہ ازیں، اگر نقشے میں ایک محدود فہرست شامل ہوتی ہے، تو نقشہ حجمی ابتدائ کے لئے نقشے کو ابتدائ کرنے کے لئے نقشہ حجمی مصروف ہوتا ہے۔

تجویز نہیں کی جاتی ہے:

m := make(map[T1]T2, 3)
m[k1] = v1
m[k2] = v2
m[k3] = v3

تجویز کی جاتی ہے:

m := map[T1]T2{
  k1: v1,
  k2: v2,
  k3: v3,
}

بنیادی رہنمائی یہ ہے کہ، ابتدائ کرتے وقت، نقشے کو ابتدائ کرنے کے لئے نقشہ کی ہنڈیاں کا استعمال کریں۔ اس کے علاوہ، (اور اگر ممکن ہو، نقشہ کی سائز کو وضاحت دیں۔)

Printf-Style فنکشنز کے لئے سٹرنگ فارمیٹ

اگر آپ کسی Printf-style فنکشن کے فارمیٹ سٹرنگ کو فنکشن کے باہر مقرر کرتے ہیں، تو اسے const لازمی طور پر ثابت قیمت کے طور پر قرار دیں۔

یہ go vet کو فارمیٹ سٹرنگ پر روایتی تجزیہ کرنے میں مدد فراہم کرتا ہے۔

تجویز نہیں کی جاتی ہے:

msg := "unexpected values %v, %v\n"
fmt.Printf(msg, 1, 2)

تجویز کی جاتی ہے:

const msg = "unexpected values %v, %v\n"
fmt.Printf(msg, 1, 2)

Printf-Style فنکشن کے نامکی

جب بھی Printf-style فنکشنز کو دعوت کریں، تو یہ یقینی بنائیں کہ go vet فارمیٹ اسٹرنگ کو دوڑ کر چیک کر سکتا ہے۔

اس کا مطلب ہے کہ آپ کو ممکنہ حد تک مقررہ Printf-style فنکشن کے نام استعمال کرنا چاہئے۔ go vet کی دیفالٹ طور پر ان کو چیک کرے گا۔ مزید معلومات کے لئے، Printf Family دیکھیں۔

اگر پہلے سے مقررہ نام استعمال نہیں کیا جا سکتا، تو منتخب نام کو f کے ساتھ ختم کر دیں: مثلاً Wrapf بجائے Wrap۔ go vet خاص Printf-style ناموں کی چیک کرنے کا درخواست کر سکتا ہے، مگر نام کو مسلسل f سے ختم ہونا چاہئے۔

go vet -printfuncs=wrapf,statusf