@@ -1148,7 +1035,7 @@ t := i.(string)
```go
t, ok := i.(string)
if !ok {
- // handle the error gracefully
+ // به خوبی خطا را مدیریت کنید
}
```
@@ -1158,14 +1045,12 @@ if !ok {
-### Don't Panic
+### از ایجاد Panic جلوگیری کنید (Don't Panic)
-Code running in production must avoid panics. Panics are a major source of
-[cascading failures](https://en.wikipedia.org/wiki/Cascading_failure). If an error occurs, the function must return an error and
-allow the caller to decide how to handle it.
+کدهایی که در محیط تولید (Production) اجرا میشوند، باید از وقوع (Panic) جلوگیری کنند. panicها عامل اصلی ایجاد [شکستهای متوالی(آبشاری)](https://en.wikipedia.org/wiki/Cascading_failure) هستند. اگر خطایی رخ دهد، تابع باید یک خطای مناسب را برگردانده و به فراخواننده (caller) اجازه دهد تا تصمیم بگیرد که چگونه با آن برخورد کند.
-Bad | Good |
+بد | خوب |
@@ -1204,20 +1089,15 @@ func main() {
|
-Panic/recover is not an error handling strategy. A program must panic only when
-something irrecoverable happens such as a nil dereference. An exception to this is
-program initialization: bad things at program startup that should abort the
-program may cause panic.
+استفاده از panic/recover به عنوان یک استراتژی مدیریت خطا مناسب نمیباشد. یک برنامه فقط زمانی باید panic کند که چیزی غیرقابل بازیابی اتفاق بیفتد (مثلاً nil dereference). یک استثنا، مقداردهی اولیه برنامه است: شرایط نامطلوبی که باعث می شود برنامه در هنگام شروع به کار متوقف شود، ممکن است باعث panic شود.
```go
var _statusTemplate = template.Must(template.New("name").Parse("_statusHTML"))
```
-
-Even in tests, prefer `t.Fatal` or `t.FailNow` over panics to ensure that the
-test is marked as failed.
+حتی در تستها، `t.Fatal` یا `t.FailNow` را به panics ترجیح دهید تا مطمئن شوید که آزمون بهعنوان ناموفق علامتگذاری شده است.
-Bad | Good |
+بد | خوب |
@@ -1244,17 +1124,14 @@ if err != nil {
|
-### Use go.uber.org/atomic
+### از پکیج `go.uber.org/atomic` استفاده کنید
-Atomic operations with the [sync/atomic](https://golang.org/pkg/sync/atomic/) package operate on the raw types
-(`int32`, `int64`, etc.) so it is easy to forget to use the atomic operation to
-read or modify the variables.
+از عملیات اتمی بسته [sync/atomic](https://golang.org/pkg/sync/atomic/) برای کار بر روی انواع اولیه (`int32`, `int64` و غیره.) استفاده کنید، بنابراین، ممکن است این نکته از یاد برود که برای دسترسی یا تغییر متغیرها، باید از عملیاتهای اتمیک استفاده کرد.
-[go.uber.org/atomic](https://godoc.org/go.uber.org/atomic) adds type safety to these operations by hiding the
-underlying type. Additionally, it includes a convenient `atomic.Bool` type.
+بسته [go.uber.org/atomic](https://godoc.org/go.uber.org/atomic) ایمنی نوع را با پنهان کردن نوع زیرین به این عملیاتها اضافه میکند. به علاوه، این بسته شامل یک تایپ `atomic.Bool` نیز میشود.
-Bad | Good |
+بد | خوب |
@@ -1299,13 +1176,12 @@ func (f *foo) isRunning() bool {
|
-### Avoid Mutable Globals
+### از متغیرهای سراسری تغییرپذیر (Mutable Globals) خودداری کنید
-Avoid mutating global variables, instead opting for dependency injection.
-This applies to function pointers as well as other kinds of values.
+از تزریق وابستگی (Dependency Injection) بجای تغییر متغیرهای سراسری استفاده کنید. این مورد روی اشارهگرهای تابع (function pointers) و همچنین برای انواع مقادیر دیگر نیز اعمال میشود.
-Bad | Good |
+بد | خوب |
@@ -1376,38 +1252,33 @@ func TestSigner(t *testing.T) {
|
-### Avoid Embedding Types in Public Structs
+### از جاسازی نوع ها (Embedding Types) در ساختارهای عمومی خودداری کنید
-These embedded types leak implementation details, inhibit type evolution, and
-obscure documentation.
+نوعهای جاسازی شده (embedded types) جزئیات اطلاعات پیادهسازی را فاش میکنند، توسعه تایپ را دشوارتر میکنند و از وضوح مستندات میکاهند.
-Assuming you have implemented a variety of list types using a shared
-`AbstractList`, avoid embedding the `AbstractList` in your concrete list
-implementations.
-Instead, hand-write only the methods to your concrete list that will delegate
-to the abstract list.
+فرض کنید شما انواع مختلفی از لیستها را با استفاده از یک `AbstractList` مشترک پیادهسازی کردهاید. از تعبیه کردن (embedding) `AbstractList` در پیادهسازیهای خاص لیستهای خود پرهیز کنید. به جای آن، تنها متدهایی را به صورت دستی برای لیست خاص خود ایجاد کنید که به `AbstractList` ارجاع میدهند.
```go
type AbstractList struct {}
-// Add adds an entity to the list.
+// Add یک موجودیت را به لیست اضافه می کند.
func (l *AbstractList) Add(e Entity) {
// ...
}
-// Remove removes an entity from the list.
+// Remove یک موجودیت را از لیست حذف می کند.
func (l *AbstractList) Remove(e Entity) {
// ...
}
```
-Bad | Good |
+بد | خوب |
```go
-// ConcreteList is a list of entities.
+// ConcreteList لیستی از موجودیت ها است.
type ConcreteList struct {
*AbstractList
}
@@ -1416,17 +1287,17 @@ type ConcreteList struct {
|
```go
-// ConcreteList is a list of entities.
+// ConcreteList لیستی از موجودیت ها است.
type ConcreteList struct {
list *AbstractList
}
-// Add adds an entity to the list.
+// Add یک موجودیت را به لیست اضافه می کند.
func (l *ConcreteList) Add(e Entity) {
l.list.Add(e)
}
-// Remove removes an entity from the list.
+// Remove یک موجودیت را از لیست حذف می کند.
func (l *ConcreteList) Remove(e Entity) {
l.list.Remove(e)
}
@@ -1435,37 +1306,27 @@ func (l *ConcreteList) Remove(e Entity) {
|
-Go allows [type embedding](https://golang.org/doc/effective_go.html#embedding) as a compromise between inheritance and composition.
-The outer type gets implicit copies of the embedded type's methods.
-These methods, by default, delegate to the same method of the embedded
-instance.
+زبان Go امکان [type embedding](https://golang.org/doc/effective_go.html#embedding) را به عنوان یک توافق بین ارثبری و ترکیب فراهم میکند. نوع بیرونی (outer type) نسخههای ضمنی از متدهای نوع تعبیهشده را به طور ضمنی به ارث میبرد و این متدها به طور پیشفرض به متد مشابه در نمونه تعبیهشده ارجاع داده میشوند.
-The struct also gains a field by the same name as the type.
-So, if the embedded type is public, the field is public.
-To maintain backward compatibility, every future version of the outer type must
-keep the embedded type.
+همچنین، ساختار (struct) فیلدی با همان نام نوع تعبیه شده را دریافت می کند. بنابراین، اگر نوع تعبیهشده عمومی باشد، فیلد عمومی است. برای حفظ توانایی کار کردن کدهای قدیمی با نسخههای جدید، هر نسخه بعدی از نوع خارجی باید نوع تعبیه شده را حفظ کند.
-An embedded type is rarely necessary.
-It is a convenience that helps you avoid writing tedious delegate methods.
+نیاز به تعبیه (embedding) نوعها به ندرت پیش میآید. این یک روش مفید است که به شما کمک میکند از نوشتن متدهای دستوری بلند و پیچیده جلوگیری کنید.
-Even embedding a compatible AbstractList *interface*, instead of the struct,
-would offer the developer more flexibility to change in the future, but still
-leak the detail that the concrete lists use an abstract implementation.
+حتی اگر یک رابط (_interface_) AbstractList سازگار را جایگزین ساختار (struct) کنید، به توسعهدهنده امکان بیشتری برای تغییر در آینده ارائه میدهد، اما همچنان جزئیات استفاده از یک پیادهسازی انتزاعی برای concrete لیستها را فاش میکند.
-Bad | Good |
+بد | خوب |
```go
-// AbstractList is a generalized implementation
-// for various kinds of lists of entities.
+// AbstractList یک پیاده سازی کلی از لیست های موجودیت های مختلف است.
type AbstractList interface {
Add(Entity)
Remove(Entity)
}
-// ConcreteList is a list of entities.
+// ConcreteList لیستی از موجودیت ها است.
type ConcreteList struct {
AbstractList
}
@@ -1474,17 +1335,17 @@ type ConcreteList struct {
|
```go
-// ConcreteList is a list of entities.
+// ConcreteList لیستی از موجودیت ها است.
type ConcreteList struct {
list AbstractList
}
-// Add adds an entity to the list.
+// Add یک موجودیت را به لیست اضافه می کند.
func (l *ConcreteList) Add(e Entity) {
l.list.Add(e)
}
-// Remove removes an entity from the list.
+// Remove یک موجودیت را از لیست حذف می کند.
func (l *ConcreteList) Remove(e Entity) {
l.list.Remove(e)
}
@@ -1493,43 +1354,34 @@ func (l *ConcreteList) Remove(e Entity) {
|
-Either with an embedded struct or an embedded interface, the embedded type
-places limits on the evolution of the type.
+استفاده از ساختارهای تعبیه شده (embedded struct) یا رابطهای تعبیه شده (embedded interface)، تکامل و توسعه Typeها را محدود می کند.
-- Adding methods to an embedded interface is a breaking change.
-- Removing methods from an embedded struct is a breaking change.
-- Removing the embedded type is a breaking change.
-- Replacing the embedded type, even with an alternative that satisfies the same
- interface, is a breaking change.
+- افزودن متدها به یک رابط تعبیهشده تغییرات مخربی ایجاد میکند.
+- حذف متدها از یک ساختار تعبیهشده نیز تغییر مخربی محسوب میشود.
+- حذف نوع تعبیهشده همچنین به عنوان یک تغییر مخرب در نظر گرفته میشود.
+- حتی جایگزین کردن نوع تعبیهشده با یک نوع جایگزین که همان رابط مشابه را پیادهسازی میکند، تغییر مخربی به حساب میآید.
-Although writing these delegate methods is tedious, the additional effort hides
-an implementation detail, leaves more opportunities for change, and also
-eliminates indirection for discovering the full List interface in
-documentation.
+اگرچه نوشتن این متدها (متدهای تعبیهشده) کمی زمانبر است، اما تلاش اضافی که برای این کار صرف میشود، باعث میشود جزئیات پیادهسازی متدها پنهان شوند. همچنین، این کار فرصتهای بیشتری برای تغییر در آینده فراهم میکند و همچنین این کار به بهبود پایداری و قابلیت تغییر بیشتر کد کمک میکند و به از بین بردن انحرافات و پیچیدگیهای غیر ضروری در مستندات کمک میکند.
-### Avoid Using Built-In Names
+### از استفاده از نام های داخلی (Buit-In) خودداری کنید
-The Go [language specification](https://golang.org/ref/spec) outlines several built-in,
-[predeclared identifiers](https://golang.org/ref/spec#Predeclared_identifiers) that should not be used as names within Go programs.
+[مشخصات زبان](https://golang.org/ref/spec) Go چندین شناسه داخلی و [شناسههای از پیش اعلام شده](https://golang.org/ref/spec#Predeclared_identifiers) را مشخص می کند که نباید در پروژه های Go استفاده شوند.
-Depending on context, reusing these identifiers as names will either shadow
-the original within the current lexical scope (and any nested scopes) or make
-affected code confusing. In the best case, the compiler will complain; in the
-worst case, such code may introduce latent, hard-to-grep bugs.
+بسته به زمینه (context)، استفاده مجدد از این شناسهها به عنوان نام، شناسه اصلی را در محدوده فعلی (یا هر محدوده تودرتو) پنهان میکند یا کد را مبهم میکند. در بهترین حالت، کامپایلر یک خطا ایجاد میکند؛ در بدترین حالت، چنین کدی ممکن است خطاهایی را ایجاد کند که بازیابی آنها دشوار است.
-Bad | Good |
+بد | خوب |
```go
var error string
-// `error` shadows the builtin
+// استفاده از نام `error` به عنوان یک متغیر یا شناسه، باعث ایجاد سایه روی نام داخلی `error` میشود.
-// or
+// یا
func handleErrorMessage(error string) {
- // `error` shadows the builtin
+ // استفاده از نام `error` به عنوان یک متغیر یا شناسه، باعث ایجاد سایه روی نام داخلی `error` میشود.
}
```
@@ -1537,12 +1389,12 @@ func handleErrorMessage(error string) {
```go
var errorMessage string
-// `error` refers to the builtin
+// در اینجا `error` به عنوان یک متغیر یا شناسه داخلی در نظر گرفته میشود
// or
func handleErrorMessage(msg string) {
- // `error` refers to the builtin
+ // در اینجا `error` به عنوان یک متغیر یا شناسه داخلی در نظر گرفته میشود
}
```
@@ -1551,23 +1403,18 @@ func handleErrorMessage(msg string) {
```go
type Foo struct {
- // While these fields technically don't
- // constitute shadowing, grepping for
- // `error` or `string` strings is now
- // ambiguous.
+// اگرچه این فیلدها از لحاظ فنی سایهزنی (shadowing) را ایجاد نمیکنند، اما جستجوی رشتههای `error` یا `string` در این موارد اکنون مبهم است.
error error
string string
}
func (f Foo) Error() error {
- // `error` and `f.error` are
- // visually similar
+ // `error` و `f.error` از نظر بصری مشابه هم هستند.
return f.error
}
func (f Foo) String() string {
- // `string` and `f.string` are
- // visually similar
+ // `string` و `f.string` از نظر بصری مشابه هم هستند.
return f.string
}
```
@@ -1576,8 +1423,7 @@ func (f Foo) String() string {
```go
type Foo struct {
- // `error` and `string` strings are
- // now unambiguous.
+ // `error` و `string` اکنون واضح هستند.
err error
str string
}
@@ -1594,33 +1440,21 @@ func (f Foo) String() string {
|
-Note that the compiler will not generate errors when using predeclared
-identifiers, but tools such as `go vet` should correctly point out these and
-other cases of shadowing.
+توجه داشته باشید که کامپایلر هنگام استفاده از شناسههای پیشتعریف شده خطا ایجاد نمی کند، اما ابزارهایی مانند `go vet` به درستی به مشکلات ضمنی در این موارد و موارد دیگر اشاره می کنند.
-### Avoid `init()`
+### از تابع `()init` استفاده نکنید
-Avoid `init()` where possible. When `init()` is unavoidable or desirable, code
-should attempt to:
+در صورت امکان، از `()init` پرهیز کنید. وقتی لازم به استفاده از تابع `()init` هستید، کد باید تلاش کند:
-1. Be completely deterministic, regardless of program environment or invocation.
-2. Avoid depending on the ordering or side-effects of other `init()` functions.
- While `init()` ordering is well-known, code can change, and thus
- relationships between `init()` functions can make code brittle and
- error-prone.
-3. Avoid accessing or manipulating global or environment state, such as machine
- information, environment variables, working directory, program
- arguments/inputs, etc.
-4. Avoid I/O, including both filesystem, network, and system calls.
+1. بدون توجه به محیط برنامه یا نحوه فراخوانی، کاملاً قطعی عمل کند.
+2. سعی کنید از ایجاد وابستگی به ترتیب اجرا یا اثرات جانبی مربوط به توابع `()init` دیگر خودداری کنید.هرچند ترتیب اجرای توابع `()init` به خوبی شناخته شده است، اما کد ممکن است تغییر کند و این وابستگیها میتوانند منجر به ناپایداری و خطاهای پنهان شوند.
+3. از دسترسی یا دستکاری وضعیت سراسری یا محیطی مانند اطلاعات ماشین، متغیرهای محیطی، دایرکتوری کاری، آرگومانها/ورودیهای برنامه و غیره پرهیز کند.
+4. از عملیات ورود/خروج (I/O) مانند عملیات فایلسیستم، شبکه و تماسهای سیستمی پرهیز کند.
-Code that cannot satisfy these requirements likely belongs as a helper to be
-called as part of `main()` (or elsewhere in a program's lifecycle), or be
-written as part of `main()` itself. In particular, libraries that are intended
-to be used by other programs should take special care to be completely
-deterministic and not perform "init magic".
+کدی که نمیتواند این موارد را انجام دهد، احتمالاً بهتر است به عنوان یک تابع کمکی برای فراخوانی در `()main` (یا در جای دیگر در چرخه عمر برنامه) قرار گیرد یا به عنوان بخشی از خود `()main` نوشته شود. به خصوص کتابخانههایی که برای استفاده در برنامههای دیگر طراحی شدهاند، باید مراقبتهای خاصی را برای تضمین قطعیت کامل داشته باشند و از "جادوی `init`" پرهیز کنند.
-Bad | Good |
+بد | خوب |
@@ -1645,7 +1479,7 @@ var _defaultFoo = Foo{
// ...
}
-// or, better, for testability:
+// یا برای تست پذیری بهتر:
var _defaultFoo = defaultFoo()
@@ -1667,10 +1501,10 @@ type Config struct {
var _config Config
func init() {
- // Bad: based on current directory
+ // بد: بر اساس دایرکتوری فعلی
cwd, _ := os.Getwd()
- // Bad: I/O
+ // بد: I/O
raw, _ := os.ReadFile(
path.Join(cwd, "config", "config.yaml"),
)
@@ -1705,24 +1539,20 @@ func loadConfig() Config {
|
-Considering the above, some situations in which `init()` may be preferable or
-necessary might include:
+با توجه به موارد فوق، شرایطی وجود دارد که ممکن است `()init` ارجح یا ضروری باشد، که ممکن است شامل موارد زیر باشد:
-- Complex expressions that cannot be represented as single assignments.
-- Pluggable hooks, such as `database/sql` dialects, encoding type registries, etc.
-- Optimizations to [Google Cloud Functions](https://cloud.google.com/functions/docs/bestpractices/tips#use_global_variables_to_reuse_objects_in_future_invocations) and other forms of deterministic
- precomputation.
+- عبارات پیچیده که نمیتوانند به عنوان انتسابهای تکی نمایان شوند. (مثلا اگر یک متغیر را نمیتوان با یک عبارت ساده از نوع x := value مقداردهی کرد و نیاز به انجام محاسبات پیچیدهتری دارید، در این صورت ممکن است از ()init استفاده کنید.)
+- قلاب های قابل اتصال، مانند پایگاه داده/sql، رجیستری نوع رمزگذاری و غیره.
+- بهینهسازیها برای [Google Cloud توابع](https://cloud.google.com/functions/docs/bestpractices/tips#use_global_variables_to_reuse_objects_in_future_invocations) و سایر اشکال پیش محاسبه قطعی.
-### Exit in Main
+### خروج فقط در تابع اصلی (Main)
-Go programs use [`os.Exit`](https://golang.org/pkg/os/#Exit) or [`log.Fatal*`](https://golang.org/pkg/log/#Fatal) to exit immediately. (Panicking
-is not a good way to exit programs, please [don't panic](#dont-panic).)
+برنامههای Go برای خروج فوری از برنامه از [`os.Exit`](https://golang.org/pkg/os/#Exit) یا [`log.Fatal*`](https://golang.org/pkg/log/#Fatal) استفاده میکنند. (استفاده از panic به عنوان روشی برای خروج از برنامه مناسب نیست، لطفاً [از panic استفاده نکنید](#dont-panic).)
-Call one of `os.Exit` or `log.Fatal*` **only in `main()`**. All other
-functions should return errors to signal failure.
+**تنها در تابع `()main`** از یکی از `os.Exit` یا `log.Fatal*` استفاده کنید. توابع دیگر برای اعلام شکست باید خطاها را به عنوان نتیجه برگردانند.
-Bad | Good |
+بد | خوب |
@@ -1776,28 +1606,20 @@ func readFile(path string) (string, error) {
|
-Rationale: Programs with multiple functions that exit present a few issues:
+در اصل: برنامههایی که دارای تعدادی تابع هستند که دارای توابع خروج فوری هستند، چندین مسئله را با خود به همراه دارند:
-- Non-obvious control flow: Any function can exit the program so it becomes
- difficult to reason about the control flow.
-- Difficult to test: A function that exits the program will also exit the test
- calling it. This makes the function difficult to test and introduces risk of
- skipping other tests that have not yet been run by `go test`.
-- Skipped cleanup: When a function exits the program, it skips function calls
- enqueued with `defer` statements. This adds risk of skipping important
- cleanup tasks.
+- جریان کنترل ناپیدا: هر تابعی ممکن است باعث خروج برنامه شود، به همین دلیل تبدیل به یک موضوع سخت برای استدلال در مورد کنترل جریان میشود.
+- دشواری در تست: توابعی که برنامه را خارج میکنند باعث میشوند فرآیند تست متوقف شود. این باعث میشود که تست کردن این توابع دشوار شود و منجر به خطر از دست دادن تستهای دیگری که هنوز توسط `go test` اجرا نشدهاند، شود.
+- پاکسازی نادیده گرفته شده: وقتی یک تابع باعث خروج برنامه میشود، اجرای توابعی که با استفاده از عبارتهای `defer` ثبت شدهاند را نادیده میگیرد. این کار باعث افزایش خطر از دست دادن وظایف پایانی مهم میشود.
-#### Exit Once
+#### فقط یکبار از یکی از توابع خروج استفاده کنید (Exit Once)
-If possible, prefer to call `os.Exit` or `log.Fatal` **at most once** in your
-`main()`. If there are multiple error scenarios that halt program execution,
-put that logic under a separate function and return errors from it.
+در صورت امکان، **حداکثر یک بار** `os.Exit` یا `log.Fatal` را در تابع `()main` خود فراخوانی کنید. اگر چندین حالت خطا وجود دارد که اجرای برنامه را متوقف میکنند، این منطق را در یک تابع مستقل قرار دهید و از آنجا خطاها را برگردانید.
-This has the effect of shortening your `main()` function and putting all key
-business logic into a separate, testable function.
+این کار باعث کوتاه شدن تابع `()main` شما میشود و همه منطق اصلی کسبوکار را در یک تابع مستقل قرار میدهد که قابلیت تست آن را بهبود میدهد.
-Bad | Good |
+بد | خوب |
@@ -1817,8 +1639,7 @@ func main() {
}
defer f.Close()
- // If we call log.Fatal after this line,
- // f.Close will not be called.
+ // اگر بعد از این خط log.Fatal را فراخوانی کنیم، f.Close فراخوانی نمی شود.
b, err := io.ReadAll(f)
if err != nil {
@@ -1865,8 +1686,7 @@ func run() error {
|
-The example above uses `log.Fatal`, but the guidance also applies to
-`os.Exit` or any library code that calls `os.Exit`.
+مثال بالا از `log.Fatal` استفاده می کند، اما این راهنما میتواند برای `os.Exit` یا هر کد کتابخانه ای که `os.Exit` را فراخوانی می کند نیز اعمال می شود.
```go
func main() {
@@ -1877,10 +1697,7 @@ func main() {
}
```
-You may alter the signature of `run()` to fit your needs.
-For example, if your program must exit with specific exit codes for failures,
-`run()` may return the exit code instead of an error.
-This allows unit tests to verify this behavior directly as well.
+شما میتوانید امضای تابع `()run` را مطابق با نیازهای خود تغییر دهید. به عنوان مثال، اگر برنامه شما باید با کدهای خروج خاصی برای خطاها خارج شود، `()run` میتواند به جای یک خطا، کد خروج را برگرداند. همچنین به تست های واحد اجازه می دهد تا مستقیماً این رفتار را تأیید کنند.
```go
func main() {
@@ -1892,27 +1709,21 @@ func run() (exitCode int) {
}
```
-More generally, note that the `run()` function used in these examples
-is not intended to be prescriptive.
-There's flexibility in the name, signature, and setup of the `run()` function.
-Among other things, you may:
+لطفاً توجه داشته باشید که تابع `()run` مورد استفاده در این مثالها استفاده شده است اجباری نیست. نام، امضا و تنظیمات تابع `()run` انعطاف پذیر هستند. از جمله موارد دیگر، می توانید:
-- accept unparsed command line arguments (e.g., `run(os.Args[1:])`)
-- parse command line arguments in `main()` and pass them onto `run`
-- use a custom error type to carry the exit code back to `main()`
-- put business logic in a different layer of abstraction from `package main`
+- آرگومانهای خط فرمان تجزیه نشده (unparsed) را میپذیرد به عنوان مثال (`run(os.Args[1:])`)
+- آرگومانهای خط فرمان را در `()main` تجزیه کنید (parse) و آنها را برای اجرا ارسال کنید
+- با استفاده از تعریف یک تایپ خطای سفارشی، کد خروج را به `()main` برگردانید
+- منطق کسب و کار را در لایههای انتزاعی مختلف `(package main)بسته اصلی` قرار دهید
-This guidance only requires that there's a single place in your `main()`
-responsible for actually exiting the process.
+با این راهنمود، تنها یک مکان در تابع `()main` شما وجود دارد که واقعاً مسئول خروج از پروسه است.
-### Use field tags in marshaled structs
+### از برچسب های فیلد در ساختارهای مارشال شده (marshaled) استفاده کنید
-Any struct field that is marshaled into JSON, YAML,
-or other formats that support tag-based field naming
-should be annotated with the relevant tag.
+هر فیلدی که به فرمتهایی مانند JSON، YAML یا سایر فرمتهایی که از نامگذاری بر اساس تگها پشتیبانی میکنند، باید با تگ مربوطه مشخص شود.
-Bad | Good |
+بد | خوب |
@@ -1934,7 +1745,7 @@ bytes, err := json.Marshal(Stock{
type Stock struct {
Price int `json:"price"`
Name string `json:"name"`
- // Safe to rename Name to Symbol.
+ // ایمن برای تغییر نام به نماد.
}
bytes, err := json.Marshal(Stock{
@@ -1946,40 +1757,25 @@ bytes, err := json.Marshal(Stock{
|
-Rationale:
-The serialized form of the structure is a contract between different systems.
-Changes to the structure of the serialized form--including field names--break
-this contract. Specifying field names inside tags makes the contract explicit,
-and it guards against accidentally breaking the contract by refactoring or
-renaming fields.
+گوروتینها سبک هستند، اما رایگان نیستند: حداقل هزینههایی را برای استفاده از حافظه برای پشته آنها و زمان CPU برای زمانبندی آنها دارند. این هزینهها در موارد معمولی کمترین تأثیر را دارند، اما زمانی که تعداد زیادی گوروتین بدون مدیریت صحیح ایجاد میشوند، میتوانند به مشکلات عملکردی بزرگی منجر شوند. همچنین، گوروتینهایی که مدیریت زمانهای چرخه حیات مشخصی ندارند، میتوانند به مشکلات دیگری نیز منجر شوند، مثل جلوگیری از جمعآوری زبالهها (garbage collected) و نگهداشتن منابعی که دیگر استفاده نمیشوند.
-### Don't fire-and-forget goroutines
+### گوروتین ها را به حال خودشان (بدون نظارت) رها نکنید
-Goroutines are lightweight, but they're not free:
-at minimum, they cost memory for their stack and CPU to be scheduled.
-While these costs are small for typical uses of goroutines,
-they can cause significant performance issues
-when spawned in large numbers without controlled lifetimes.
-Goroutines with unmanaged lifetimes can also cause other issues
-like preventing unused objects from being garbage collected
-and holding onto resources that are otherwise no longer used.
+گوروتینها سبک هستند، اما رایگان نیستند: حداقل هزینههایی را برای استفاده از حافظه برای پشته آنها و زمان CPU برای زمانبندی آنها دارند. این هزینهها در موارد معمولی کمترین تأثیر را دارند، اما زمانی که تعداد زیادی گوروتین بدون مدیریت صحیح ایجاد میشوند، میتوانند به مشکلات عملکردی بزرگی منجر شوند. همچنین، گوروتینهایی که بدون مدیریت زمانهای چرخه حیات مشخصی ایجاد میشوند، میتوانند به مشکلات دیگری نیز منجر شوند، مثل جلوگیری از جمعآوری زبالهها (garbage collected) و نگهداشتن منابعی که دیگر استفاده نمیشوند.
-Therefore, do not leak goroutines in production code.
-Use [go.uber.org/goleak](https://pkg.go.dev/go.uber.org/goleak)
-to test for goroutine leaks inside packages that may spawn goroutines.
+بنابراین، از لو رفتن (leak) گوروتینها در کد تولیدی (production code) جلوگیری کنید. برای تست نشتی گوروتین داخل پکیجهایی که ممکن است گوروتین ایجاد کنند، از [go.uber.org/goleak](https://pkg.go.dev/go.uber.org/goleak) استفاده کنید.
-In general, every goroutine:
+بطور کلی، هر گوروتین باید:
-- must have a predictable time at which it will stop running; or
-- there must be a way to signal to the goroutine that it should stop
+- یک زمان پیشبینیشده برای متوقف شدن داشته باشد؛ یا
+- باید یک راه برای اعلام به گوروتین وجود داشته باشد که باید متوقف شود.
-In both cases, there must be a way code to block and wait for the goroutine to
-finish.
+در هر دو مورد، باید یک روش وجود داشته باشد تا کد بلاک شده و منتظر اتمام گوروتین شود.
-For example:
+برای مثال:
-Bad | Good |
+بد | خوب |
@@ -1996,8 +1792,8 @@ go func() {
```go
var (
- stop = make(chan struct{}) // tells the goroutine to stop
- done = make(chan struct{}) // tells us that the goroutine exited
+ stop = make(chan struct{}) // به گوروتین می گوید که متوقف شود
+ done = make(chan struct{}) // به ما می گوید که گوروتین خارج شد
)
go func() {
defer close(done)
@@ -2014,33 +1810,28 @@ go func() {
}
}()
-// Elsewhere...
-close(stop) // signal the goroutine to stop
-<-done // and wait for it to exit
+// خارج از محدوده گوروتین(در جایی دیگر)...
+close(stop) // به گوروتین علامت دهید که متوقف شود
+<-done // و صبر کنید تا خارج شود
```
|
-There's no way to stop this goroutine.
-This will run until the application exits.
+هیچ راهی برای متوقف کردن این گوروتین وجود ندارد. گوروتین تا زمانی که برنامه خارج شود اجرا می شود.
|
-This goroutine can be stopped with `close(stop)`,
-and we can wait for it to exit with `<-done`.
+این گوروتین را می توان با `close(stop)` متوقف کرد، و می توانیم منتظر خروج آن با `done->` باشیم.
|
-#### Wait for goroutines to exit
+#### منتظر خروج گوروتین ها باشید
-Given a goroutine spawned by the system,
-there must be a way to wait for the goroutine to exit.
-There are two popular ways to do this:
+با توجه به گوروتین ایجاد شده توسط سیستم، باید راهی برای انتظار خروج گوروتین وجود داشته باشد. دو روش رایج برای انجام این کار وجود دارد:
-- Use a `sync.WaitGroup`.
- Do this if there are multiple goroutines that you want to wait for
+- اگر چندین گوروتین دارید که میخواهید منتظر آنها بمانید از `sync.WaitGroup` استفاده کنید.
```go
var wg sync.WaitGroup
@@ -2052,12 +1843,11 @@ There are two popular ways to do this:
}()
}
- // To wait for all to finish:
+ // صبر کنید تا همه چیز تمام شود:
wg.Wait()
```
-- Add another `chan struct{}` that the goroutine closes when it's done.
- Do this if there's only one goroutine.
+- اگر تنها یک گوروتین وجود دارد، بهتر است یک کانال `{}chan struct` دیگر ایجاد کنید که گوروتین آن را پس از انجام کار ببندد. به این ترتیب میتوانید انتظار برای اتمام گوروتین را داشته باشید. این کار به شما اجازه میدهد تا بدون استفاده از `sync.WaitGroup` منتظر اتمام گوروتین باشید.
```go
done := make(chan struct{})
@@ -2066,23 +1856,21 @@ There are two popular ways to do this:
// ...
}()
- // To wait for the goroutine to finish:
+ // برای اینکه منتظر بمانید تا کار گوروتین تمام شود:
<-done
```
-#### No goroutines in `init()`
+#### از گوروتین ها در تابع `()init` استفاده نکنید
`init()` functions should not spawn goroutines.
See also [Avoid init()](#avoid-init).
-If a package has need of a background goroutine,
-it must expose an object that is responsible for managing a goroutine's
-lifetime.
-The object must provide a method (`Close`, `Stop`, `Shutdown`, etc)
-that signals the background goroutine to stop, and waits for it to exit.
+توابع `()init` نباید گوروتینها را راهاندازی کنند. همچنین دیگر مواردی که استفاده از [تابع ()init را توصیه نمیکند](#avoid-init):
+
+اگر یک بسته (package) نیاز به یک گوروتین پسزمینه دارد، باید یک شی ارائه دهد که مسئولیت مدیریت چرخه حیات گوروتین را بر عهده دارد. این شی باید یک متد (مانند `Close`, `Stop`, `Shutdown` و غیره) ارائه دهد که به گوروتین پسزمینه اعلام کند که باید متوقف شود و منتظر اتمام آن بماند.
-Bad | Good |
+بد | خوب |
@@ -2121,8 +1909,7 @@ func (w *Worker) doWork() {
}
}
-// Shutdown tells the worker to stop
-// and waits until it has finished.
+// خاموش شدن (Shutdown) به workder می گوید که متوقف شود و صبر کند تا کار تمام شود.
func (w *Worker) Shutdown() {
close(w.stop)
<-w.done
@@ -2132,33 +1919,29 @@ func (w *Worker) Shutdown() {
|
-Spawns a background goroutine unconditionally when the user exports this package.
-The user has no control over the goroutine or a means of stopping it.
+زمانی که کاربر این بسته (package) را (export) میکند، یک گوروتین پسزمینه بدون شرطی ایجاد میشود.
+کاربر هیچ کنترلی بر روی گوروتین ندارد و هیچ وسیلهای برای متوقف کردن آن وجود ندارد.
+
|
-Spawns the worker only if the user requests it.
-Provides a means of shutting down the worker so that the user can free up
-resources used by the worker.
+گوروتین، worker را فقط در صورتی ایجاد میکند که کاربر آن را درخواست کند. همچنین امکانی برای shutdownکردن worker فراهم میکند تا کاربر بتواند منابع مورد استفاده توسط worker را آزاد کند.
-Note that you should use `WaitGroup`s if the worker manages multiple
-goroutines.
-See [Wait for goroutines to exit](#wait-for-goroutines-to-exit).
+توجه داشته باشید که اگر worker مدیریت چندین گوروتین را انجام میدهد، باید از `WaitGroups` استفاده کنید. برای جزئیات بیشتر به [منتظر اتمام گوروتینها باشید](#منتظر-خروج-گوروتین-ها-باشید)
|
-## Performance
+## کارایی (Performance)
-Performance-specific guidelines apply only to the hot path.
+دستورالعملهای مربوط به عملکرد، تنها به مسیر اصلی (hot path) اعمال میشوند.
-### Prefer strconv over fmt
+### پکیج `strconv` را به `fmt` ترجیح دهید
-When converting primitives to/from strings, `strconv` is faster than
-`fmt`.
+وقتی میخواهید primitives را به string تبدیل کنید یا برعکس، بهتر است از بسته `strconv` استفاده کنید چرا که عملکرد این بسته سریعتر از بسته `fmt` است.
-Bad | Good |
+بد | خوب |
@@ -2192,10 +1975,9 @@ BenchmarkStrconv-4 64.2 ns/op 1 allocs/op
|
-### Avoid string-to-byte conversion
+### از تبدیل رشته به بایت (string-to-byte) خودداری کنید
-Do not create byte slices from a fixed string repeatedly. Instead, perform the
-conversion once and capture the result.
+به طور مکرر برشهای بایت (byte slices) را از stringهای ثابت ایجاد نکنید. بجای اینکار، یک تبدیل انجام دهید و نتیجه را ثبت کنید.
Bad | Good |
@@ -2233,32 +2015,24 @@ BenchmarkGood-4 500000000 3.25 ns/op
-### Prefer Specifying Container Capacity
+### ترجیحا ظرفیت کانتینر (container) را مشخص کنید
-Specify container capacity where possible in order to allocate memory for the
-container up front. This minimizes subsequent allocations (by copying and
-resizing of the container) as elements are added.
+تا جایی که امکان دارد ظرفیت کانتینر را مشخص کنید تا حافظه از قبل برای کانتینر تخصیص داده شود. این امر تخصیص های بعدی (با کپی و تغییر اندازه ظرف) را هنگام افزودن عناصر به حداقل می رساند.
-#### Specifying Map Capacity Hints
+#### تعیین حداکثر ظرفیت ممکن Map
-Where possible, provide capacity hints when initializing
-maps with `make()`.
+در صورت امکان، هنگام مقداردهی اولیه Mapها با `()make` اندازه ظرفیت آن را مشخص کنید.
```go
make(map[T1]T2, hint)
```
-Providing a capacity hint to `make()` tries to right-size the
-map at initialization time, which reduces the need for growing
-the map and allocations as elements are added to the map.
+مشخص کردن ظرفیت به ()make باعث ایجاد Map در زمان مقداردهی اولیه میشود، که در صورت اضافه شدن عناصر به Map، از تخصیص مجدد حافظه برای Map جلوگیری میکند.
-Note that, unlike slices, map capacity hints do not guarantee complete,
-preemptive allocation, but are used to approximate the number of hashmap buckets
-required. Consequently, allocations may still occur when adding elements to the
-map, even up to the specified capacity.
+در واقعیت، تعیین ظرفیت Map با استفاده از تابع ()make نمیتواند به صورت دقیق و کامل تعداد دقیق buckets مورد نیاز برای یک hashmap را پیشبینی کند. به جای اینکه به صورت کامل پیشبینی شده باشد، این تعیین ظرفیت تقریبا buckets مورد نیاز برای hashmap را ارائه میدهد. به عبارت دیگر، حتی با تعیین یک ظرفیت خاص، ممکن است در هنگام افزودن عناصر به Map، تخصیصها (allocation) انجام شود.
-Bad | Good |
+بد | خوب |
@@ -2286,34 +2060,27 @@ for _, f := range files {
|
-`m` is created without a size hint; there may be more
-allocations at assignment time.
+متغیر `m` بدون تعیین اندازه ایجاد شده است؛ بنابراین ممکن است در زمان اختصاص (assignment) عناصر به `m` تخصیصهای بیشتری ایجاد شود.
|
-`m` is created with a size hint; there may be fewer
-allocations at assignment time.
+متغیر `m` با یک اشاره به اندازه ایجاد شده است؛ بنابراین ممکن است در زمان اختصاص (assignment) عناصر به `m` تخصیصهای کمتری ایجاد شود.
|
-#### Specifying Slice Capacity
+#### تعیین ظرفیت برش(slice)
-Where possible, provide capacity hints when initializing slices with `make()`,
-particularly when appending.
+در صورت امکان، هنگام مقداردهی اولیه sliceها با استفاده از تابع `()make`، مقدار ظرفیت راتعیین کنید، به ویژه هنگام اضافه کردن عناصر.
```go
make([]T, length, capacity)
```
-Unlike maps, slice capacity is not a hint: the compiler will allocate enough
-memory for the capacity of the slice as provided to `make()`, which means that
-subsequent `append()` operations will incur zero allocations (until the length
-of the slice matches the capacity, after which any appends will require a resize
-to hold additional elements).
+برخلاف Mapها، ظرفیت برش(Slice) نیازی به مشخص کردن ظرفیت آرایه در زمان ایجاد آن ندارد: به این معنا که عملیاتهای بعدی `()append` هیچ تخصیص حافظهای را در پی ندارند (تا زمانی که طول آرایه با ظرفیت مطابقت داشته باشد، پس از آن هر append به منظور نگهداری عناصر اضافی نیاز به تغییر اندازه دارد).
-Bad | Good |
+بد | خوب |
@@ -2353,44 +2120,32 @@ BenchmarkGood-4 100000000 0.21s
|
-## Style
+## استایل (style)
-### Avoid overly long lines
+### از خطوط بیش از حد طولانی خودداری کنید
-Avoid lines of code that require readers to scroll horizontally
-or turn their heads too much.
+از خطوط کدی که خوانندگان را ملزم به اسکرول افقی یا چرخاندن بیش از حد سر خود می کند اجتناب کنید.
-We recommend a soft line length limit of **99 characters**.
-Authors should aim to wrap lines before hitting this limit,
-but it is not a hard limit.
-Code is allowed to exceed this limit.
+ما محدودیت طول خط نرم **99 کاراکتر** را توصیه می کنیم. نویسندگان باید قبل از رسیدن به این حد، خطوط را wrap کنند، اما این یک محدودیت دقیق نیست. اجازه داده شده است که کد این محدودیت را تجاوز کند.
-### Be Consistent
+### یکپارچگی را رعایت کنید
-Some of the guidelines outlined in this document can be evaluated objectively;
-others are situational, contextual, or subjective.
+برخی از معیارهای ذکر شده در این مقاله، ارزیابی های عینی، بر اساس موقعیت یا سناریو، زمینه (context)، یا قضاوت های ذهنی هستند.
-Above all else, **be consistent**.
+مهمتر از همه اینا، **پیوستگی را حفظ کنید**.
-Consistent code is easier to maintain, is easier to rationalize, requires less
-cognitive overhead, and is easier to migrate or update as new conventions emerge
-or classes of bugs are fixed.
+کد یکنواخت و یکدست راحتتر ویرایش میشود، منطقیتر است، نیاز به تفکر کمتری دارد، و همچنین راحتتر میتوان آن را بهروز کرد و رفع اشکالها در آن آسانتر است.
-Conversely, having multiple disparate or conflicting styles within a single
-codebase causes maintenance overhead, uncertainty, and cognitive dissonance,
-all of which can directly contribute to lower velocity, painful code reviews,
-and bugs.
+به عبارت دیگر، داشتن چندین سبک مختلف کدنویسی در یک پایگاه کد میتواند منجر به هزینههای سربار تعمیر و نگهداری، عدم انسجام و عدم تطابق در استایلها یا نگارش کد میشود در نهایت همه اینها مستقیماً منجر به کاهش سرعت، بررسی کدهای پیچیده و افزایش تعداد اشکال میشود.
-When applying these guidelines to a codebase, it is recommended that changes
-are made at a package (or larger) level: application at a sub-package level
-violates the above concern by introducing multiple styles into the same code.
+هنگام اعمال این استانداردها در یک codebase، توصیه میشود که تغییرات در سطح پکیج (یا بزرگتر) اعمال شود، با اجرای این تغییرات در سطح زیربسته (sub-package) میتواند نگرانیهای فوق را با معرفی چندین سبک در یک کد نقض کند.
-### Group Similar Declarations
+### تعاریف مشابه را گروه بندی کنید
-Go supports grouping similar declarations.
+زبان Go از گروهبندی اعلانهای مشابه پشتیبانی میکند.
-Bad | Good |
+بد | خوب |
@@ -2411,10 +2166,10 @@ import (
|
-This also applies to constants, variables, and type declarations.
+همین امر در مورد ثابتها، متغیرها و اعلان تایپها صدق میکند:
-Bad | Good |
+بد | خوب |
@@ -2456,10 +2211,10 @@ type (
|
-Only group related declarations. Do not group declarations that are unrelated.
+فقط اعلانهای مرتبط را گروهبندی کنید. اعلانهای غیرمرتبط را گروهبندی نکنید.
-Bad | Good |
+بد | خوب |
@@ -2491,11 +2246,10 @@ const EnvVar = "MY_ENV"
|
-Groups are not limited in where they can be used. For example, you can use them
-inside of functions.
+هیچ محدودیتی برای استفاده از گروهها وجود ندارد، به عنوان مثال: میتوانید از آنها در داخل توابع استفاده کنید:
-Bad | Good |
+بد | خوب |
@@ -2526,12 +2280,10 @@ func f() string {
|
-Exception: Variable declarations, particularly inside functions, should be
-grouped together if declared adjacent to other variables. Do this for variables
-declared together even if they are unrelated.
+استثنا: اعلانهای متغیر (مخصوصاً آنهایی که درون توابع هستند) در صورت مجاورت با متغیرهای دیگر باید با هم گروهبندی شوند. این کار را برای متغیرهای اعلام شده با هم انجام دهید، حتی اگر نامرتبط باشند.
-Bad | Good |
+بد | خوب |
@@ -2564,17 +2316,16 @@ func (c *client) request() {
|
-### Import Group Ordering
+### مرتب سازی گروهی واردات (imports)
-There should be two import groups:
+واردات باید به دو دسته تقسیم شود:
-- Standard library
-- Everything else
-
-This is the grouping applied by goimports by default.
+- کتابخانه استاندارد
+- سایر کتابخانه ها
+این گروه بندی است که توسط goimports به طور پیش فرض اعمال می شود.
-Bad | Good |
+بد | خوب |
@@ -2602,30 +2353,26 @@ import (
|
-### Package Names
+### نام گذاری بسته ها (Package Names)
-When naming packages, choose a name that is:
+هنگام نامگذاری بستهها(packages)، نامی را انتخاب کنید که:
-- All lower-case. No capitals or underscores.
-- Does not need to be renamed using named imports at most call sites.
-- Short and succinct. Remember that the name is identified in full at every call
- site.
-- Not plural. For example, `net/url`, not `net/urls`.
-- Not "common", "util", "shared", or "lib". These are bad, uninformative names.
+- تمام حروف کوچک. بدون حروف بزرگ یا زیرخط.
+- در بیشتر موارد هنگام استفاده از واردات نامگذاری شده، تغییر نام مورد نیاز نیست.
+- کوتاه و مختصر باشد. به یاد داشته باشید که نام را در هر کجا که استفاده می شود کاملاً مشخص کنید.
+نیازی به جمع نیست. به عنوان مثال net/url، نه net/urls.
+- از "common"، "util"، "shared" یا "lib" استفاده نکنید. اینها اسامی بد و بی معنی هستند.
-See also [Package Names](https://blog.golang.org/package-names) and [Style guideline for Go packages](https://rakyll.org/style-packages/).
+همچنین [راهنمای نامگذاری بسته(Package Names)](https://blog.golang.org/package-names) و [راهنمای استایل بسته(package) Go](https://rakyll.org/style-packages/) را ببینید.
-### Function Names
+### نام گذاری توابع (Function Names)
-We follow the Go community's convention of using [MixedCaps for function
-names](https://golang.org/doc/effective_go.html#mixed-caps). An exception is made for test functions, which may contain underscores
-for the purpose of grouping related test cases, e.g.,
+ما از روش رایج جامعه Go با استفاده از [MixedCaps برای نامگذاری توابع](https://golang.org/doc/effective_go.html#mixed-caps) پیروی میکنیم. یک استثناء برای توابع تست وجود دارد که ممکن است شامل زیرخط (_) به منظور گروهبندی موارد تست مرتبط باشد، به عنوان مثال،
`TestMyFunction_WhatIsBeingTested`.
-### Import Aliasing
+### نام مستعار واردات (Import)
-Import aliasing must be used if the package name does not match the last
-element of the import path.
+در صورتی که نام پکیج با آخرین بخش مسیر (import path) مطابقت نداشته باشد، باید از نامگذاری مخفف (aliasing) برای import استفاده شود.
```go
import (
@@ -2636,11 +2383,10 @@ import (
)
```
-In all other scenarios, import aliases should be avoided unless there is a
-direct conflict between imports.
+در سایر موارد، باید از نامگذاری مخفف (aliasing) در import خودداری شود مگر اینکه تداخل مستقیمی بین imports وجود داشته باشد.
-Bad | Good |
+بد | خوب |
@@ -2669,22 +2415,19 @@ import (
|
-### Function Grouping and Ordering
+### گروه بندی و مرتب سازی توابع
-- Functions should be sorted in rough call order.
-- Functions in a file should be grouped by receiver.
+- توابع باید بر اساس تقریبی فراخوانیشان مرتب شوند(منظور این است که توابعی که بیشترین احتمال برای فراخوانی آنها وجود دارد در ابتدا آورده میشوند و توابعی که کمترین احتمال فراخوانی رو دارند در انتها قرار میگیرند).
+- توابع موجود در یک فایل باید بر اساس گیرنده (receiver) گروه بندی شوند(منظور این است که توابعی که بر روی یک نوع خاص عمل میکنند، در یک قسمت مشخص از کد قرار میگیرند. این کار به ترتیب و منظم شدن کدها کمک میکند و به توسعه دهندگان کمک میکند تا توابع مرتبط با هم را به راحتی پیدا کنند).
-Therefore, exported functions should appear first in a file, after
-`struct`, `const`, `var` definitions.
+بنابراین، توابع صادرشده (exported) باید بعد از تعریفهای `struct`, `const`, `var` در ابتدای فایل ظاهر شوند.
-A `newXYZ()`/`NewXYZ()` may appear after the type is defined, but before the
-rest of the methods on the receiver.
+توابعی که با `()newXYZ`/`()NewXYZ` شروع میشوند، ممکن است بعد از تعریف نوع (type) قبل از باقی متدهای دریافتکننده (receiver) ظاهر شوند.
-Since functions are grouped by receiver, plain utility functions should appear
-towards the end of the file.
+از آنجایی که توابع توسط گیرنده (receiver) گروهبندی میشوند، توابع utility باید در انتهای فایل ظاهر شوند.
-Bad | Good |
+بد | خوب |
@@ -2725,14 +2468,12 @@ func calcCost(n []int) int {...}
|
-### Reduce Nesting
+### تورفتگی (Nesting) را کاهش دهید
-Code should reduce nesting where possible by handling error cases/special
-conditions first and returning early or continuing the loop. Reduce the amount
-of code that is nested multiple levels.
+کد باید سعی کند تورفتگی (nesting) را به حداقل برساند. برای این کار، ابتدا موارد خطا یا شرایط خاص را بررسی و پردازش کند و در صورت لزوم به سرعت از تابع خارج شود یا به مرحله بعد بروند. همچنین باید تلاش کند تا تعداد کدهایی که به چندین سطح تورفتگی وارد میشوند را کاهش دهد.
-Bad | Good |
+بد | خوب |
@@ -2771,13 +2512,12 @@ for _, v := range data {
|
-### Unnecessary Else
+### اجتناب از Elseهای غیر ضروری
-If a variable is set in both branches of an if, it can be replaced with a
-single if.
+اگر یک متغیر در هر دو شاخه (شرط true و شرط false) یک دستور if تنظیم میشود، میتوانید از یک if تنها استفاده کنید.
-Bad | Good |
+بد | خوب |
@@ -2802,13 +2542,12 @@ if b {
|
-### Top-level Variable Declarations
+### تعاریف متغیرهای سطح بالا
-At the top level, use the standard `var` keyword. Do not specify the type,
-unless it is not the same type as the expression.
+در ابتدای کد، از واژه کلیدی معمول `var` استفاده کنید. در صورتی که نوع متغیر مطابق نوع عبارت مقداردهی باشد، نیازی به مشخص کردن نوع نیست.
-Bad | Good |
+بد | خوب |
@@ -2822,8 +2561,7 @@ func F() string { return "A" }
```go
var _s = F()
-// Since F already states that it returns a string, we don't need to specify
-// the type again.
+// از آنجایی که F قبلاً بیان می کند که یک رشته را برمی گرداند، نیازی به تعیین مجدد نوع آن نداریم.
func F() string { return "A" }
```
@@ -2831,8 +2569,7 @@ func F() string { return "A" }
|
-Specify the type if the type of the expression does not match the desired type
-exactly.
+اگر نوع عبارت دقیقاً با نوع مورد نیاز مطابقت ندارد، نوع آن را مشخص کنید.
```go
type myError struct{}
@@ -2842,20 +2579,17 @@ func (myError) Error() string { return "error" }
func F() myError { return myError{} }
var _e error = F()
-// F returns an object of type myError but we want error.
+// F یک شی از نوع myError را برمی گرداند اما ما خطا می خواهیم.
```
-### Prefix Unexported Globals with _
+### از پیشوند `"_"` برای متغیرهای خصوصی (Unexported) استفاده کنید
-Prefix unexported top-level `var`s and `const`s with `_` to make it clear when
-they are used that they are global symbols.
+به منظور افزایش دقت و وضوح، متغیرها(`var`s) و ثابتهایی(`const`s) که عموماً در سطح بالای کد (یعنی در دسترسی پکیج) قرار میگیرند، با استفاده از نشانه "_" (زیرخط) قبل از نام آنها ترکیب شوند. این کار باعث میشود که هنگام استفاده از آنها در دیگر بخشهای کد، به وضوح متوجه شود که این متغیرها و ثابتها به عنوان نمادهای سراسری (global) در نظر گرفته شوند.
-Rationale: Top-level variables and constants have a package scope. Using a
-generic name makes it easy to accidentally use the wrong value in a different
-file.
+دلیل: متغیرها و ثابتهای سطح بالا در محدودهی پکیج قرار دارند و تا حدی کلی هستند. استفاده از نامهای عمومی ممکن است باعث اشتباه در استفاده از مقادیر اشتباه در فایلهای دیگر شود.
-Bad | Good |
+بد | خوب |
@@ -2874,8 +2608,7 @@ func Bar() {
...
fmt.Println("Default port", defaultPort)
- // We will not see a compile error if the first line of
- // Bar() is deleted.
+ // اگر خط اول Bar() حذف شود، خطای کامپایل نخواهیم دید.
}
```
@@ -2893,17 +2626,14 @@ const (
|
-**Exception**: Unexported error values may use the prefix `err` without the underscore.
-See [Error Naming](#error-naming).
+**استثنا**: در مواردی که مقادیر خطا (error) به صورت unexported باشند، میتوانید از پیشوند `err` بدون خط زیر (underscore) استفاده کنید. به منظور اطلاعات بیشتر در مورد نامگذاری خطا، به [نامگذاری خطا](#نام-گذاری-خطا) مراجعه کنید.
-### Embedding in Structs
+### جاسازی (Embedding) در ساختارها
-Embedded types should be at the top of the field list of a
-struct, and there must be an empty line separating embedded fields from regular
-fields.
+اگر نوعهای تو در تو (embedded types) در یک struct وجود دارند، آنها باید در بالای لیست فیلدهای struct قرار گیرند، و باید یک خط خالی بین فیلدهای تو در تو و فیلدهای معمولی وجود داشته باشد.
-Bad | Good |
+بد | خوب |
@@ -2927,46 +2657,36 @@ type Client struct {
|
-Embedding should provide tangible benefit, like adding or augmenting
-functionality in a semantically-appropriate way. It should do this with zero
-adverse user-facing effects (see also: [Avoid Embedding Types in Public Structs](#avoid-embedding-types-in-public-structs)).
-
-Exception: Mutexes should not be embedded, even on unexported types. See also: [Zero-value Mutexes are Valid](#zero-value-mutexes-are-valid).
-
-Embedding **should not**:
-
-- Be purely cosmetic or convenience-oriented.
-- Make outer types more difficult to construct or use.
-- Affect outer types' zero values. If the outer type has a useful zero value, it
- should still have a useful zero value after embedding the inner type.
-- Expose unrelated functions or fields from the outer type as a side-effect of
- embedding the inner type.
-- Expose unexported types.
-- Affect outer types' copy semantics.
-- Change the outer type's API or type semantics.
-- Embed a non-canonical form of the inner type.
-- Expose implementation details of the outer type.
-- Allow users to observe or control type internals.
-- Change the general behavior of inner functions through wrapping in a way that
- would reasonably surprise users.
-
-Simply put, embed consciously and intentionally. A good litmus test is, "would
-all of these exported inner methods/fields be added directly to the outer type";
-if the answer is "some" or "no", don't embed the inner type - use a field
-instead.
+درج نوعهای دیگر در یک ساختار (embedding) باید به صورتی باشد که به ویژگیها یا قابلیتها به یک شکل منطقی و معنادار افزوده یا تقویت کند. این کار باید بدون تأثیر منفی قابل مشاهده برای کاربران انجام شود (برای اطلاعات بیشتر، "همچنین: [از جاسازی نوعها (Embedding Types) در ساختارهای عمومی خودداری کنید](#از-جاسازی-نوع-ها-embedding-types-در-ساختارهای-عمومی-خودداری-کنید)" را ببینید).
+
+استثناء: حتی در نوعهای (unexported) هم، Mutexها (قفلهای همزمانی) نباید به صورت تعبیه شده درج شوند. همچنین میتوانید به [مقدار صفر (zero-value) Mutexها معتبر هستند](#مقدار-صفر-zero-value-mutexها-معتبر-هستند) مراجعه کنید.
+
+تعبیه (Embedding) **نباید:**
+
+- صرفا به منظور زیبایی یا افزایش راحتی باشد.
+- ساختن یا استفاده از نوعهای خارجی را پیچیدهتر کند.
+- باعث تغییر در مقدار-صفر (zero value) نوع خارجی شود. . اگر نوع خارجی، مقدار صفر مفیدی دارد، پس از تعبیه نوع داخلی، همچنان باید مقدار صفر مفید داشته باشد.
+- توابع یا فیلدهای غیرمرتبط از نوع خارجی را به عنوان نتیجه تعبیه نمایش دهد.
+- نوعهای (unexported) را نمایش دهد.
+- اثرات کپی (copy) انواع خارجی را تغییر دهد.
+- API یا معناشناسی انواع خارجی را تغییر دهد.
+- یک نمایش غیرمعمول از نوع داخلی را ارائه دهد.
+- جزئیات پیادهسازی نوع خارجی را نشان دهد.
+- به کاربران اجازه مشاهده یا کنترل اطلاعات داخلی نوع را بدهد.
+- با تغییر رفتار کلی عملکردهای داخلی موقعیتهای غیرمنتظره ای را برای کاربران به ارمغان بیاورد.
+
+بطور کلی، تعبیه (Embedding) باید با آگاهی و هدف انجام شود. یک آزمون ساده برای این کار این است: "آیا تمام این متدها/فیلدها باید به صورت مستقیم به نوع خارجی اضافه شوند؟" اگر پاسخ "بله" باشد، معقول است که تعبیه انجام شود؛ اگر پاسخ "بخشی از آنها" یا "خیر" باشد، بهتر است از یک فیلد به جای تعبیه استفاده کنید.
-Bad | Good |
+بد | خوب |
```go
type A struct {
- // Bad: A.Lock() and A.Unlock() are
- // now available, provide no
- // functional benefit, and allow
- // users to control details about
- // the internals of A.
+// بد: حالا دستورهای A.Lock() و A.Unlock() در دسترس هستند،
+// اما فایدهای ندارند و به کاربران اجازه میدهند که
+// جزئیات داخلی A را کنترل کنند.
sync.Mutex
}
```
@@ -2975,10 +2695,9 @@ type A struct {
```go
type countingWriteCloser struct {
- // Good: Write() is provided at this
- // outer layer for a specific
- // purpose, and delegates work
- // to the inner type's Write().
+// خوب: تابع Write() در این لایه بیرونی برای
+// یک هدف خاص فراهم شده است و کار را به
+// تابع Write() نوع داخلی انتقال میدهد.
io.WriteCloser
count int
@@ -2995,7 +2714,7 @@ func (w *countingWriteCloser) Write(bs []byte) (int, error) {
```go
type Book struct {
- // Bad: pointer changes zero value usefulness
+ // بد: اشارهگر سودمندی مقدار-صفر را تغییر میدهد
io.ReadWriter
// other fields
@@ -3013,7 +2732,7 @@ b.Write(...) // panic: nil pointer
```go
type Book struct {
- // Good: has useful zero value
+ // خوب: دارای مقدار-صفر مفید است
bytes.Buffer
// other fields
@@ -3053,13 +2772,12 @@ type Client struct {
|
-### Local Variable Declarations
+### تعاریف متغیرهای محلی
-Short variable declarations (`:=`) should be used if a variable is being set to
-some value explicitly.
+اگر یک متغیر به صراحت به یک مقدار تنظیم میشود، باید از اعلانهای کوتاه متغیر (`:=`) استفاده شود.
-Bad | Good |
+بد | خوب |
@@ -3076,11 +2794,10 @@ s := "foo"
|
-However, there are cases where the default value is clearer when the `var`
-keyword is used. [Declaring Empty Slices](https://github.com/golang/go/wiki/CodeReviewComments#declaring-empty-slices), for example.
+با این حال، مواردی وجود دارد که در آن مقدار پیشفرض وقتی که از واژه کلیدی var استفاده میشود، واضحتر است. برای مثال، در [اعلان برشهای خالی](https://github.com/golang/go/wiki/CodeReviewComments#declaring-empty-slices) (Empty Slices).
-Bad | Good |
+بد | خوب |
@@ -3111,15 +2828,14 @@ func f(list []int) {
|
-### nil is a valid slice
+### خود `nil` یک برش `slice` معتبر است
-`nil` is a valid slice of length 0. This means that,
+ خود `nil` به عنوان یک برش با طول صفر (length 0) معتبر شناخته میشود. این بدان معناست که:
-- You should not return a slice of length zero explicitly. Return `nil`
- instead.
+- شما نباید به صورت صریح یک برش با طول صفر را برگردانید. به جای آن باید `nil` را برگردانید.
- Bad | Good |
+ بد | خوب |
@@ -3140,11 +2856,10 @@ func f(list []int) {
|
-- To check if a slice is empty, always use `len(s) == 0`. Do not check for
- `nil`.
+- برای بررسی اینکه آیا یک برش (slice) خالی است یا نه، همیشه از عبارت `len(s) == 0` استفاده کنید.نباید برای بررسی خالی بودن از `nil` استفاده کنید.
- Bad | Good |
+ بد | خوب |
@@ -3165,11 +2880,10 @@ func f(list []int) {
|
-- The zero value (a slice declared with `var`) is usable immediately without
- `make()`.
+- مقدار صفر (یک برش که با `var` اعلان شده است) بدون نیاز به استفاده از تابع `make()`، بلافاصله قابل استفاده است.
- Bad | Good |
+ بد | خوب |
@@ -3203,17 +2917,14 @@ func f(list []int) {
|
-Remember that, while it is a valid slice, a nil slice is not equivalent to an
-allocated slice of length 0 - one is nil and the other is not - and the two may
-be treated differently in different situations (such as serialization).
+به خاطر داشته باشید که در حالی که یک برش nil معتبر است، اما با یک برش تخصیص داده شده با طول صفر معادل نیست - یکی از آنها nil است و دیگری نیست - و در شرایط مختلف (مانند فرآیند سریالسازی) ممکن است به صورت متفاوتی مدیریت شوند.
-### Reduce Scope of Variables
+### کاهش دامنه (scope) متغیرها
-Where possible, reduce scope of variables. Do not reduce the scope if it
-conflicts with [Reduce Nesting](#reduce-nesting).
+در صورت امکان، سعی کنید دامنه متغیرها را محدود کنید. مگر اینکه با قانون [تورفتگی (Nesting) را کاهش دهید](#تورفتگی-nesting-را-کاهش-دهید) در تضاد باشد.
-Bad | Good |
+بد | خوب |
@@ -3235,11 +2946,10 @@ if err := os.WriteFile(name, data, 0644); err != nil {
|
-If you need a result of a function call outside of the if, then you should not
-try to reduce the scope.
+اگر نتیجه یک تابع را بیرون از شرط if نیاز دارید، در اینصورت نباید سعی کنید دامنه متغیر را کاهش دهید.
-Bad | Good |
+بد | خوب |
@@ -3276,13 +2986,12 @@ return nil
|
-### Avoid Naked Parameters
+### از پارامترهای بی نام (Naked Parameters) خودداری کنید
-Naked parameters in function calls can hurt readability. Add C-style comments
-(`/* ... */`) for parameter names when their meaning is not obvious.
+پارامترهای بینام در فراخوانی توابع میتوانند خوانایی را کاهش دهند. در صورتی که معنای پارامترها واضح نباشد، نامهای پارامترها را با کامنت استایل C (`/* ... */`) اضافه کنید.
-Bad | Good |
+بد | خوب |
@@ -3303,9 +3012,7 @@ printInfo("foo", true /* isLocal */, true /* done */)
|
-Better yet, replace naked `bool` types with custom types for more readable and
-type-safe code. This allows more than just two states (true/false) for that
-parameter in the future.
+بهتر است پارامترهای `bool` بدون نوع خاص را با نوعهای سفارشی جایگزین کنید تا کد خواناتر و ایمنتری داشته باشید. این امکان را به شما میدهد تا در آینده بیش از دو وضعیت (true/false) برای این پارامتر داشته باشید.
```go
type Region int
@@ -3320,20 +3027,18 @@ type Status int
const (
StatusReady Status = iota + 1
StatusDone
- // Maybe we will have a StatusInProgress in the future.
+ // شاید در آینده StatusInProgress داشته باشیم.
)
func printInfo(name string, region Region, status Status)
```
-### Use Raw String Literals to Avoid Escaping
+### استفاده از `Raw String Literals` برای جلوگیری از Escape شدن کاراکترها
-Go supports [raw string literals](https://golang.org/ref/spec#raw_string_lit),
-which can span multiple lines and include quotes. Use these to avoid
-hand-escaped strings which are much harder to read.
+زبان Go از رشتههای متنی خام ([raw string literals](https://golang.org/ref/spec#raw_string_lit)) پشتیبانی میکند. این نوع رشتهها میتوانند از چندین خط تشکیل شده و شامل نقل قولها باشند. برای افزایش خوانایی کد و جلوگیری از استفاده از رشتههای دستساز با ویژگیهای خاص، از رشتههای متنی خام استفاده کنید. این نوع رشتهها خوانایی کد را خیلی بالا میبرند.
-Bad | Good |
+بد | خوب |
@@ -3350,15 +3055,14 @@ wantError := `unknown error:"test"`
|
-### Initializing Structs
+### مقداردهی اولیه ساختارها (structs)
-#### Use Field Names to Initialize Structs
+#### استفاده از نام فیلدها برای مقداردهی اولیه ساختارها
-You should almost always specify field names when initializing structs. This is
-now enforced by [`go vet`](https://golang.org/cmd/vet/).
+تقریباً همیشه باید نام فیلدها را هنگام مقداردهی اولیه ساختارها (structs) مشخص کنید. این توصیه اکنون توسط ابزار [`go vet`](https://golang.org/cmd/vet/) اجباری شده است.
-Bad | Good |
+بد | خوب |
@@ -3379,8 +3083,7 @@ k := User{
|
-Exception: Field names *may* be omitted in test tables when there are 3 or
-fewer fields.
+استثنا: زمانی که تعداد فیلدها سه یا کمتر باشد میتوانید نام فیلدها را در جداول تست حذف کنید.
```go
tests := []struct{
@@ -3392,14 +3095,12 @@ tests := []struct{
}
```
-#### Omit Zero Value Fields in Structs
+#### حذف فیلدهای مقدارصفر (zero value) در ساختارها
-When initializing structs with field names, omit fields that have zero values
-unless they provide meaningful context. Otherwise, let Go set these to zero
-values automatically.
+در هنگام مقداردهی اولیه به ساختارها (structs) با استفاده از نام فیلدها، فیلدهایی که مقدار صفر (zero value) دارند را حذف کنید مگر اینکه به دلایل معناداری نیاز به آنها داشته باشید. در غیر این صورت، به Go اجازه دهید این فیلدها را به طور خودکار به مقادیر صفر تنظیم کند.
-Bad | Good |
+بد | خوب |
@@ -3424,12 +3125,9 @@ user := User{
|
-This helps reduce noise for readers by omitting values that are default in
-that context. Only meaningful values are specified.
+این به کاهش نویز برای خوانندگان با حذف مقادیر پیشفرض در آن زمینه کمک میکند. فقط مقادیر معنی دار مشخص شده است.
-Include zero values where field names provide meaningful context. For example,
-test cases in [Test Tables](#test-tables) can benefit from names of fields
-even when they are zero-valued.
+در صورتی که نام فیلدها مفهومی داشته باشند، مقادیر صفر (zero values) را نیز در نظر بگیرید. به عنوان مثال، در [جداول تست (Table-driven tests)](#جداول-تست-table-driven-tests)، استفاده از نام فیلدها حتی زمانی که این مقادیر صفری (zero values) هستند میتواند مفید باشد.
```go
tests := []struct{
@@ -3441,13 +3139,12 @@ tests := []struct{
}
```
-#### Use `var` for Zero Value Structs
+#### استفاده از `var` برای ساختارهای مقدارصفر (zero value)
-When all the fields of a struct are omitted in a declaration, use the `var`
-form to declare the struct.
+زمانی که تمامی فیلدهای یک ساختار (struct) در یک اعلان حذف شدند، از شکل `var` برای اعلان ساختار استفاده کنید.
-Bad | Good |
+بد | خوب |
@@ -3464,24 +3161,21 @@ var user User
|
-This differentiates zero valued structs from those with non-zero fields
-similar to the distinction created for [map initialization](#initializing-maps), and matches how
-we prefer to [declare empty slices](https://github.com/golang/go/wiki/CodeReviewComments#declaring-empty-slices).
+این کار باعث تفکیک ساختارهای با مقدار صفر از ساختارهای دارای فیلدهای غیر صفر میشود، مشابه تفکیکی که برای [مقداردهی اولیه Mapها](#مقداردهی-اولیه-mapها) ایجاد شده است، و با روش ترجیحی ما برای [اعلان برشهای خالی](https://github.com/golang/go/wiki/CodeReviewComments#declaring-empty-slices) هماهنگ میشود.
-#### Initializing Struct References
+#### مقداردهی اولیه ساختارهای رفرنس دار
-Use `&T{}` instead of `new(T)` when initializing struct references so that it
-is consistent with the struct initialization.
+از `&T{}` به جای `new(T)` هنگام مقداردهی اولیه ساختار (struct references) استفاده کنید تا با مقداردهی اولیه ساختار مطابقت داشته باشد.
-Bad | Good |
+بد | خوب |
```go
sval := T{Name: "foo"}
-// inconsistent
+// ناسازگار
sptr := new(T)
sptr.Name = "bar"
```
@@ -3497,22 +3191,19 @@ sptr := &T{Name: "bar"}
|
-### Initializing Maps
+### مقداردهی اولیه Mapها
-Prefer `make(..)` for empty maps, and maps populated
-programmatically. This makes map initialization visually
-distinct from declaration, and it makes it easy to add size
-hints later if available.
+برای ایجاد نقشههای خالی و نقشههایی که به صورت برنامهنویسی پر میشوند، استفاده از تابع `(..)make` توصیه میشود. این اقدام نه تنها مقداردهی نقشه را از اعلان آن به صورت بصری متمایز میکند، بلکه اگر در آینده اندازه (size hints) در دسترس قرار بگیرد، امکان اضافه کردن آنها را آسان میسازد.
-Bad | Good |
+بد | خوب |
```go
var (
- // m1 is safe to read and write;
- // m2 will panic on writes.
+ // m1 برای خواندن و نوشتن امن است.
+ // m2 در نوشتن panic خواهد کرد.
m1 = map[T1]T2{}
m2 map[T1]T2
)
@@ -3522,8 +3213,8 @@ var (
```go
var (
- // m1 is safe to read and write;
- // m2 will panic on writes.
+ // m1 برای خواندن و نوشتن امن است.
+ // m2 در نوشتن panic خواهد کرد.
m1 = make(map[T1]T2)
m2 map[T1]T2
)
@@ -3532,25 +3223,21 @@ var (
|
-Declaration and initialization are visually similar.
+اعلان و مقداردهی اولیه از نظر بصری مشابه هستند.
|
-Declaration and initialization are visually distinct.
+اعلان و مقداردهی اولیه از نظر بصری متمایز هستند
|
-Where possible, provide capacity hints when initializing
-maps with `make()`. See
-[Specifying Map Capacity Hints](#specifying-map-capacity-hints)
-for more information.
+در صورت امکان، هنگام مقداردهی اولیه نقشه ها با `make()` اندازه ظرفیت ارائه دهید. برای اطلاعات بیشتر به [تعیین حداکثر ظرفیت ممکن Map](#تعیین-حداکثر-ظرفیت-ممکن-map) مراجعه کنید.
-On the other hand, if the map holds a fixed list of elements,
-use map literals to initialize the map.
+از سوی دیگر، اگر نقشه مجموعهی ثابتی از عناصر را نگه میدارد، از نقشههای لیترال (map literals) برای مقداردهی اولیه استفاده کنید.
-Bad | Good |
+بد | خوب |
@@ -3574,19 +3261,16 @@ m := map[T1]T2{
|
-The basic rule of thumb is to use map literals when adding a fixed set of
-elements at initialization time, otherwise use `make` (and specify a size hint
-if available).
+قاعده اساسی در استفاده از نقشهها به شکل زیر است: اگر قرار باشد مجموعهی ثابتی از عناصر را در زمان مقداردهی اولیه اضافه کنید، از نقشههای لیترال (map literals) استفاده کنید. در غیر اینصورت، از تابع `make()` استفاده کنید (و در صورت امکان مقدار ظرفیت را مشخص کنید).
-### Format Strings outside Printf
+### قالب بندی رشته ها (strings) خارج از تابع `Printf`
-If you declare format strings for `Printf`-style functions outside a string
-literal, make them `const` values.
+اگر شما رشتههای قالببندی (format strings) برای توابع استایلدهی، مانند `Printf` را خارج از رشته معمولی اعلان میکنید، آنها را به عنوان مقادیر `const` ایجاد کنید.
-This helps `go vet` perform static analysis of the format string.
+این کمک میکند تا `go vet` تجزیه و تحلیل استاتیک رشته قالببندی را انجام دهد.
-Bad | Good |
+بد | خوب |
@@ -3605,38 +3289,30 @@ fmt.Printf(msg, 1, 2)
|
-### Naming Printf-style Functions
+### نام گذاری توابع به سبک `Printf`
-When you declare a `Printf`-style function, make sure that `go vet` can detect
-it and check the format string.
+وقتی یک تابع با استایل `Printf` اعلان میکنید، مطمئن شوید که `go vet` قادر به شناسایی آن و بررسی رشته قالببندی است.
-This means that you should use predefined `Printf`-style function
-names if possible. `go vet` will check these by default. See [Printf family](https://golang.org/cmd/vet/#hdr-Printf_family)
-for more information.
+این بدان معناست که در صورت امکان باید از نامهای پیشتعریف شده برای توابع به سبک Printf استفاده کنید. `go vet` به طور پیشفرض اینها را بررسی میکند. برای اطلاعات بیشتر، به [خانواده Printf](https://golang.org/cmd/vet/#hdr-Printf_family) مراجعه کنید.
-If using the predefined names is not an option, end the name you choose with
-f: `Wrapf`, not `Wrap`. `go vet` can be asked to check specific `Printf`-style
-names but they must end with f.
+اگر نمی توانید از یک نام از پیش تعریف شده استفاده کنید، نامی را که انتخاب می کنید با f خاتمه دهید: مثلاً `Wrapf`، بجای `Wrap`. میتوان از `go vet` بخواهیم نامهای خاص به سبک `Printf` را بررسی کند، اما باید با f خاتمه یابد.
```shell
go vet -printfuncs=wrapf,statusf
```
-See also [go vet: Printf family check](https://kuzminva.wordpress.com/2017/11/07/go-vet-printf-family-check/).
+همچنین، میتوانید به مقاله [بررسی خانواده Printf توسط go vet](https://kuzminva.wordpress.com/2017/11/07/go-vet-printf-family-check/) مراجعه کنید.
-## Patterns
+## الگوها
-### Test Tables
+### جداول تست (Table-driven tests)
-Table-driven tests with [subtests](https://blog.golang.org/subtests) can be a helpful pattern for writing tests
-to avoid duplicating code when the core test logic is repetitive.
+استفاده از الگوی تستهای جدولی با [subtests](https://blog.golang.org/subtests) میتواند یک الگوی مفید برای نوشتن تستها باشد تا از تکرار کد در زمانی که منطق آزمون اصلی تکرار میشود جلوگیری شود.
-If a system under test needs to be tested against *multiple conditions* where
-certain parts of the the inputs and outputs change, a table-driven test should
-be used to reduce redundancy and improve readability.
+اگر یک سیستم تحت آزمون تست نیاز به آزمایش در برابر _شرایط چندگانه_ دارد که در آن بخشهای خاصی از ورودیها و خروجیها تغییر میکنند، بهترین روش استفاده از تستهای جدولی است. این روش کد را کمتر تکراری میکند و خوانایی آن را بهبود میبخشد.
-Bad | Good |
+بد | خوب |
@@ -3709,12 +3385,9 @@ for _, tt := range tests {
|
-Test tables make it easier to add context to error messages, reduce duplicate
-logic, and add new test cases.
+استفاده از جداول تست (Test tables) کمک میکند تا پیامهای خطا دارای زمینه (context) بیشتری باشند، منطق تکراری را کاهش دهد و امکان افزودن تستهای جدید را فراهم کند.
-We follow the convention that the slice of structs is referred to as `tests`
-and each test case `tt`. Further, we encourage explicating the input and output
-values for each test case with `give` and `want` prefixes.
+ما از این قرارداد پیروی میکنیم که برشی از ساختارها (slice of struct) به عنوان تست `tests` مدنظر است و هر مورد آزمون `tt` نامیده میشود. علاوه بر این، ما توصیه میکنیم تا مقادیر ورودی و خروجی برای هر مورد تست را با پیشوندهای `give` و `want` به صورت صریح مشخص کنید.
```go
tests := []struct{
@@ -3730,49 +3403,31 @@ for _, tt := range tests {
}
```
-#### Avoid Unnecessary Complexity in Table Tests
+#### جلوگیری از پیچیدگیهای غیرضروری در تستهای جدولی
-Table tests can be difficult to read and maintain if the subtests contain conditional
-assertions or other branching logic. Table tests should **NOT** be used whenever
-there needs to be complex or conditional logic inside subtests (i.e. complex logic inside the `for` loop).
+اگر منطق پیچیده یا شرطی در زیرتستها وجود داشته باشد (به عبارت دیگر، منطق پیچیده در داخل حلقه `for`)، تستهای جدولی ممکن است خواندن و نگهداری دشواری داشته باشند و بهتر است از آنها **استفاده نشود**.
-Large, complex table tests harm readability and maintainability because test readers may
-have difficulty debugging test failures that occur.
+تستهای جدولی بزرگ و پیچیده به خوانایی و نگهداری آسیب میزنند زیرا افرادی که تستها را میخوانند ممکن است در اشکالزدایی خطاهایی که در تستها رخ میدهد به مشکل بخورند.
-Table tests like this should be split into either multiple test tables or multiple
-individual `Test...` functions.
+تستهای جدولی مانند این باید به یکی از دو گزینه زیر تقسیم شوند: یا به چندین جدول تست مجزا یا به چندین تابع تست مجزا با نامهای `Test...`
-Some ideals to aim for are:
+بعضی از اهدافی که باید به آنها دست یابیم عبارتند از:
-* Focus on the narrowest unit of behavior
-* Minimize "test depth", and avoid conditional assertions (see below)
-* Ensure that all table fields are used in all tests
-* Ensure that all test logic runs for all table cases
+- تمرکز بر روی بخشهای خاص و محدودی از عملکرد
+- کاهش "عمق تست" و اجتناب از ادعاهای شرطی (راهنمایی زیر را ببینید)
+- اطمینان از استفاده از همهی فیلدهای جدول در تمام تستها
+- اطمینان از اجرای منطق تست برای تمام موارد جدول
-In this context, "test depth" means "within a given test, the number of
-successive assertions that require previous assertions to hold" (similar
-to cyclomatic complexity).
-Having "shallower" tests means that there are fewer relationships between
-assertions and, more importantly, that those assertions are less likely
-to be conditional by default.
+در این متن، "عمق تست" به معنای "تعداد ادعاهای متوالی در یک تست داده شده است که نیاز به اثبات ادعاهای قبلی دارند" (مشابه به پیچیدگی سیکلوماتیک) است. داشتن "تستهای کمعمق" به این معناست که تعداد ارتباطات بین ادعاها کمتر است و بهطور مهمتر، این ادعاها به طور پیشفرض کمتر از ادعاهای شرطی هستند.
-Concretely, table tests can become confusing and difficult to read if they use multiple branching
-pathways (e.g. `shouldError`, `expectCall`, etc.), use many `if` statements for
-specific mock expectations (e.g. `shouldCallFoo`), or place functions inside the
-table (e.g. `setupMocks func(*FooMock)`).
+به طور مشخص، تستهای جدول اگر از مسیرهای انشعاب چندگانه (مانند `shouldError`، `expectCall` و غیره) استفاده کنند، از بسیاری از دستورات `if` برای انتظارات ساختگی خاص (مانند `shouldCallFoo`) استفاده کنند یا توابعی را در داخل جدول قرار دهند (مثلاً `setupMocks func (* FooMock)`) میتوانند گیجکننده باشند و درک آن دشوار شود.
-However, when testing behavior that only
-changes based on changed input, it may be preferable to group similar cases
-together in a table test to better illustrate how behavior changes across all inputs,
-rather than splitting otherwise comparable units into separate tests
-and making them harder to compare and contrast.
+با این حال، هنگام آزمایش رفتاری که فقط بر اساس ورودی تغییر یافته تغییر میکند، موارد مشابه را در یک آزمون جدول با هم گروهبندی میکنیم تا نحوه تغییر رفتار در همه ورودیها را بهتر نشان دهیم، تا اینکه واحدهای قابل مقایسه را به آزمونهای جداگانه تقسیم کنیم و انجام آنها را سختتر کنیم.
-If the test body is short and straightforward,
-it's acceptable to have a single branching pathway for success versus failure cases
-with a table field like `shouldErr` to specify error expectations.
+اگر بدنه تست کوتاه و ساده باشد، میتوانید برای موارد موفقیت و شکست، یک مسیر اجرایی (شاخه) واحد را در نظر بگیرید که از طریق یک فیلد در جدول تست، مثلاً `shouldErr` برای تعیین انتظارات خطا، انتخاب شود.
-Bad | Good |
+بد | خوب |
@@ -3852,22 +3507,17 @@ func TestShouldCallYAndFail(t *testing.T) {
assert.EqualError(t, err, "Y failed")
}
```
+
|
-This complexity makes it more difficult to change, understand, and prove the
-correctness of the test.
+این پیچیدگی باعث مشکلات در تغییر، درک و اثبات صحت تست میشود.
-While there are no strict guidelines, readability and maintainability should
-always be top-of-mind when deciding between Table Tests versus separate tests
-for multiple inputs/outputs to a system.
+اگرچه دستورالعملهای دقیقی وجود ندارد، وقتی بین استفاده از تستهای جدولی و تستهای مجزا برای ورودیها/خروجیهای متعدد به یک سیستم تصمیمگیری میکنید، همیشه باید به خوانایی و قابلیت نگهداری فکر کرد.
-#### Parallel Tests
+#### تست های موازی
-Parallel tests, like some specialized loops (for example, those that spawn
-goroutines or capture references as part of the loop body),
-must take care to explicitly assign loop variables within the loop's scope to
-ensure that they hold the expected values.
+تستهای موازی، مانند برخی از حلقههای تخصصی (برای مثال، آنهایی که گوروتینها را ایجاد میکنند یا ارجاعها را به عنوان بخشی از بدنه حلقه میگیرند)، باید دقت کنند که متغیرهای حلقه را به صراحت در محدوده حلقه تخصیص دهند تا اطمینان حاصل شود که مقادیر مورد انتظار را نگه میدارند.
```go
tests := []struct{
@@ -3886,26 +3536,18 @@ for _, tt := range tests {
}
```
-In the example above, we must declare a `tt` variable scoped to the loop
-iteration because of the use of `t.Parallel()` below.
-If we do not do that, most or all tests will receive an unexpected value for
-`tt`, or a value that changes as they're running.
+در مثال بالا، به دلیل استفاده از `t.Parallel()` در زیر حلقه، ما باید یک متغیر `tt` را در دامنه هر تکرار حلقه تعریف کنیم. اگر این کار را انجام ندهیم، بیشتر یا تمام تستها مقدار غیرمنتظرهای برای متغیر `tt` دریافت خواهند کرد یا مقداری که در حال اجرای آنها تغییر میکند.
-### Functional Options
+### الگوی Functional Options
-Functional options is a pattern in which you declare an opaque `Option` type
-that records information in some internal struct. You accept a variadic number
-of these options and act upon the full information recorded by the options on
-the internal struct.
+گزینههای عملکردی (Functional options) الگویی است که در آن یک نوع گزینه (`Option`) غیرشفاف را اعلام میکنید که اطلاعات را در یک ساختار داخلی ثبت میکند. شما تعدادی متغیر از این گزینهها را می پذیرید و بر اساس اطلاعات کاملی که توسط گزینهها در ساختار داخلی ثبت شده است، عمل میکنید.
-Use this pattern for optional arguments in constructors and other public APIs
-that you foresee needing to expand, especially if you already have three or
-more arguments on those functions.
+از این الگو برای آرگومانهای اختیاری در متد سازندهها و سایر واسطهای عمومی (API) که پیشبینی میکنید نیاز به توسعه آنها دارید، استفاده کنید ه خصوص اگر از قبل سه یا بیشتر آرگومان در این توابع داشته باشید.
-Bad | Good |
+بد | خوب |
@@ -3950,8 +3592,7 @@ func Open(
|
-The cache and logger parameters must always be provided, even if the user
-wants to use the default.
+پارامترهای کش و لاگر همیشه باید ارائه شوند، حتی اگر کاربر بخواهد از پیش فرض استفاده کند.
```go
db.Open(addr, db.DefaultCache, zap.NewNop())
@@ -3962,7 +3603,7 @@ db.Open(addr, false /* cache */, log)
|
-Options are provided only if needed.
+گزینهها (Opptions) فقط در صورت نیاز ارائه میشوند.
```go
db.Open(addr)
@@ -3978,9 +3619,7 @@ db.Open(
|
-Our suggested way of implementing this pattern is with an `Option` interface
-that holds an unexported method, recording options on an unexported `options`
-struct.
+روش پیشنهادی ما برای پیادهسازی این الگو استفاده از یک رابط (Interface) به نام `Option` است که یک متد خصوصی (unexported) را نگه میدارد و گزینهها (`options`) را در یک ساختار (struct) نیز خصوصی ثبت میکند.
```go
type options struct {
@@ -4032,44 +3671,30 @@ func Open(
}
```
-Note that there's a method of implementing this pattern with closures but we
-believe that the pattern above provides more flexibility for authors and is
-easier to debug and test for users. In particular, it allows options to be
-compared against each other in tests and mocks, versus closures where this is
-impossible. Further, it lets options implement other interfaces, including
-`fmt.Stringer` which allows for user-readable string representations of the
-options.
+توجه داشته باشید که روشی برای پیادهسازی این الگو با استفاده از توابع بسته (closures) وجود دارد، اما ما باور داریم که الگوی بالا انعطاف بیشتری برای نویسندگان فراهم میکند و اشکالزدایی و آزمایش آن برای کاربران راحتتر است. به طور خاص، این الگو اجازه میدهد که گزینهها در تستها و موکها با یکدیگر مقایسه شوند، در مقابل توابع بسته که این امکان در آنها وجود ندارد. علاوه بر این، این الگو به گزینهها امکان پیادهسازی رابطهای دیگر را میدهد، از جمله `fmt.Stringer` که امکان نمایش رشتهای خوانا از گزینهها را فراهم میکند.
-See also,
+همچنین ببینید،
-- [Self-referential functions and the design of options](https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html)
+- [توابع خود ارجاعی و طراحی گزینهها (Self-referential functions and the design of options)](https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html)
- [Functional options for friendly APIs](https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis)
-## Linting
+## بررسی و تمیز کردن (linting)
-More importantly than any "blessed" set of linters, lint consistently across a
-codebase.
+مهمتر از هر چیز، اعمال یک استاندارد یکسان در کل پروژه است، نه استفاده از یک مجموعه خاص از ابزارهای بررسی کد.
-We recommend using the following linters at a minimum, because we feel that they
-help to catch the most common issues and also establish a high bar for code
-quality without being unnecessarily prescriptive:
+توصیه میکنیم حداقل از لینترهای زیر استفاده کنید، زیرا فکر میکنیم که این ابزارها به شناسایی مشکلات رایج کمک میکنند و همچنین یک استاندارد بالا برای کیفیت کد ایجاد میکنند بدون اینکه غیرضروری تجویز شوند:
-- [errcheck](https://github.com/kisielk/errcheck) to ensure that errors are handled
-- [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports) to format code and manage imports
-- [golint](https://github.com/golang/lint) to point out common style mistakes
-- [govet](https://golang.org/cmd/vet/) to analyze code for common mistakes
-- [staticcheck](https://staticcheck.io/) to do various static analysis checks
+- [errcheck](https://github.com/kisielk/errcheck) برای اطمینان از رسیدگی به خطاها
+- [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports) برای قالب بندی کد و مدیریت واردات
+- [golint](https://github.com/golang/lint) برای اشاره به اشتباهات رایج استایل
+- [govet](https://golang.org/cmd/vet/) برای تجزیه و تحلیل کد برای اشتباهات رایج
+- [staticcheck](https://staticcheck.io/) برای انجام بررسی های مختلف آنالیز استاتیکی
### Lint Runners
-We recommend [golangci-lint](https://github.com/golangci/golangci-lint) as the go-to lint runner for Go code, largely due
-to its performance in larger codebases and ability to configure and use many
-canonical linters at once. This repo has an example [.golangci.yml](https://github.com/uber-go/guide/blob/master/.golangci.yml) config file
-with recommended linters and settings.
+ما توصیه میکنیم از [golangci-lint](https://github.com/golangci/golangci-lint) به عنوان ابزار اصلی برای اجرای عملیات lint در کد Go استفاده کنید، به دلیل عملکرد برتر آن در پروژههای بزرگ و قابلیت پیکربندی و استفاده از ابزارهای بررسی کد معتبر بسیاری به صورت همزمان. این مخزن (repo) یک فایل پیکربندی [.golangci.yml](https://github.com/uber-go/guide/blob/master/.golangci.yml) با ابزارهای بررسی کد پیشنهادی و تنظیمات راهنمایی شده را دارد.
-golangci-lint has [various linters](https://golangci-lint.run/usage/linters/) available for use. The above linters are
-recommended as a base set, and we encourage teams to add any additional linters
-that make sense for their projects.
+golangci-lint دارای [لینترهای مختلفی](https://golangci-lint.run/usage/linters/) برای استفاده است. لینترهای فوق به عنوان یک مجموعه پایه توصیه میشوند و ما تیمها را تشویق میکنیم که هر گونه لینتر اضافی را که برای پروژههایشان منطقی است اضافه کنند.
diff --git a/style_ir.md b/style_ir.md
deleted file mode 100644
index 1d7081c7..00000000
--- a/style_ir.md
+++ /dev/null
@@ -1,3700 +0,0 @@
-
-
-
-
-# راهنمای سبک برنامهنویسی شرکت اوبر (Uber) در گولنگ
-
-## [English](https://github.com/uber-go/guide/blob/master/style.md)
-
-## تغییرات و نسخه
-
-#### این مخزن به موازات نسخه اصلی آن به صورت بلادرنگ آپدیت خواهد شد. همچنین میتوانید لیست کامل تغییرات را در فایل [CHANGELOG.md](https://github.com/jamalkaksouri/uber-go-guide-ir/blob/master/CHANGELOG.md) مشاهده کنید.
-## فهرست مطالب
-- [مقدمه](#مقدمه)
-- [راهنماها](#راهنماها)
- - [ارجاع به رابطها (Pointers to Interfaces)](#ارجاع-به-رابط-ها-pointers-to-interfaces)
- - [انطباقپذیری رابطها](#انطباق-پذیری-رابط-ها)
- - [گیرندهها و رابطها (Recievers and Interfaces)](#گیرنده-ها-و-رابط-ها-recievers-and-interfaces)
- - [مقدار صفر (zero-value) Mutexها معتبر هستند](#مقدار-صفر-zero-value-mutexها-معتبر-هستند)
- - [کپی کردن بخشهای مشخص از Sliceها و Mapها](#کپی-کردن-بخش-های-مشخص-از-sliceها-و-mapها)
- - [به تعویق انداختن (Defer) پاکسازی منابع](#به-تعویق-انداختن-defer-پاکسازی-منابع)
- - [اندازه کانال (Channel) یک یا هیچ است](#اندازه-کانال-channel-یک-یا-هیچ-است)
- - [ثابتهای نامگذاری شده (Enums) را از یک شمارهگذاری کنید](#ثابت-های-نام-گذاری-شده-enums-را-از-یک-شماره-گذاری-کنید)
- - [استفاده از `"Time"` برای مدیریت زمان](#استفاده-از-time-برای-مدیریت-زمان)
- - [خطاها (Errors)](#خطاها-errors)
- - [انواع خطاها](#انواع-خطاها)
- - [بستهبندی خطا (Error Wrapping)](#بستهبندی-خطا-error-wrapping)
- - [نامگذاری خطا](#نام-گذاری-خطا)
- - [مدیریت یکباره خطاها](#مدیریت-یکباره-خطاها)
- - [مدیریت نوع ادعای (Type Assertion) شکستها](#مدیریت-نوع-ادعای-type-assertion-شکست-ها)
- - [از ایجاد Panic جلوگیری کنید (Don't Panic)](#از-ایجاد-panic-جلوگیری-کنید-dont-panic)
- - [از پکیج "go.uber.org/atomic" استفاده کنید](#از-پکیج-gouberorgatomic-استفاده-کنید)
- - [از متغیرهای سراسری تغییرپذیر (Mutable Globals) خودداری کنید](#از-متغیرهای-سراسری-تغییرپذیر-mutable-globals-خودداری-کنید)
- - [از جاسازی نوعها (Embedding Types) در ساختارهای عمومی خودداری کنید](#از-جاسازی-نوع-ها-embedding-types-در-ساختارهای-عمومی-خودداری-کنید)
- - [از استفاده از نامهای داخلی (Buit-In) خودداری کنید](#از-استفاده-از-نام-های-داخلی-buit-in-خودداری-کنید)
- - [از تابع `()init` استفاده نکنید](#از-تابع-init-استفاده-نکنید)
- - [خروج فقط در تابع اصلی (Main)](#خروج-فقط-در-تابع-اصلی-main)
- - [فقط یکبار از یکی از توابع خروج استفاده کنید (Exit Once)](#فقط-یکبار-از-یکی-از-توابع-خروج-استفاده-کنید-exit-once)
- - [از برچسبهای فیلد در ساختارهای مارشال شده (marshaled) استفاده کنید](#از-برچسب-های-فیلد-در-ساختارهای-مارشال-شده-marshaled-استفاده-کنید)
- - [گوروتینها را به حال خودشان (بدون نظارت) رها نکنید](#گوروتین-ها-را-به-حال-خودشان-بدون-نظارت-رها-نکنید)
- - [منتظر خروج گوروتینها باشید](#منتظر-خروج-گوروتین-ها-باشید)
- - [از گوروتینها در تابع `()init` استفاده نکنید](#از-تابع-init-استفاده-نکنید)
-- [کارایی (Performance)](#کارایی-performance)
- - [پکیج `strconv` را به `fmt` ترجیح دهید](#پکیج-strconv-را-به-fmt-ترجیح-دهید)
- - [از تبدیل رشته به بایت (string-to-byte) خودداری کنید](#از-تبدیل-رشته-به-بایت-string-to-byte-خودداری-کنید)
- - [ترجیحا ظرفیت کانتینر (container) را مشخص کنید](#ترجیحا-ظرفیت-کانتینر-container-را-مشخص-کنید)
-- [استایل (style)](#استایل-style)
- - [از خطوط بیش از حد طولانی خودداری کنید](#از-خطوط-بیش-از-حد-طولانی-خودداری-کنید)
- - [یکپارچگی را رعایت کنید](#یکپارچگی-را-رعایت-کنید)
- - [تعاریف مشابه را گروهبندی کنید](#تعاریف-مشابه-را-گروه-بندی-کنید)
- - [مرتبسازی گروهی واردات (imports)](#مرتب-سازی-گروهی-واردات-imports)
- - [نامگذاری بستهها (Package Names)](#نام-گذاری-بسته-ها-package-names)
- - [نامگذاری توابع (Function Names)](#نام-گذاری-توابع-function-names)
- - [نام مستعار واردات (Import)](#نام-مستعار-واردات-import)
- - [گروهبندی و مرتبسازی توابع](#گروه-بندی-و-مرتب-سازی-توابع)
- - [تورفتگی (Nesting) را کاهش دهید](#تورفتگی-nesting-را-کاهش-دهید)
- - [اجتناب از Elseهای غیر ضروری](#اجتناب-از-elseهای-غیر-ضروری)
- - [تعاریف متغیرهای سطح بالا](#تعاریف-متغیرهای-سطح-بالا)
- - [از پیشوند `"_"` برای متغیرهای خصوصی (Unexported) استفاده کنید](#از-پیشوند-_-برای-متغیرهای-خصوصی-unexported-استفاده-کنید)
- - [جاسازی (Embedding) در ساختارها](#embedding-in-structs)
- - [تعاریف متغیرهای محلی](#تعاریف-متغیرهای-محلی)
- - [خود `nil` یک برش `slice` معتبر است](#خود-nil-یک-برش-slice-معتبر-است)
- - [کاهش دامنه (scope) متغیرها](#کاهش-دامنه-scope-متغیرها)
- - [از پارامترهای بینام(Naked Parameters) خودداری کنید](#از-پارامترهای-بی-نام-naked-parameters-خودداری-کنید)
- - [استفاده از `Raw String Literals` برای جلوگیری از Escape شدن کاراکترها](#استفاده-از-raw-string-literals-برای-جلوگیری-از-escape-شدن-کاراکترها)
- - [مقداردهی اولیه ساختارها (structs)](#مقداردهی-اولیه-ساختارها-structs)
- - [استفاده از نام فیلدها برای مقداردهی اولیه ساختارها](#استفاده-از-نام-فیلدها-برای-مقداردهی-اولیه-ساختارها)
- - [حذف فیلدهای مقدارصفر (zero value) در ساختارها](#استفاده-از-var-برای-ساختارهای-مقدارصفر-zero-value)
- - [استفاده از `var` برای ساختارهای مقدارصفر (zero value)](#استفاده-از-var-برای-ساختارهای-مقدارصفر-zero-value)
- - [مقداردهی اولیه ساختارهای رفرنسدار](#مقداردهی-اولیه-ساختارهای-رفرنس-دار)
- - [مقداردهی اولیه Mapها](#مقداردهی-اولیه-mapها)
- - [قالببندی رشتهها (strings) خارج از تابع `Printf`](#قالب-بندی-رشته-ها-strings-خارج-از-تابع-printf)
- - [نامگذاری توابع به سبک `Printf`](#نام-گذاری-توابع-به-سبک-printf)
-- [الگوها](#الگوها)
- - [جداول تست (Table-driven tests)](#جداول-تست-table-driven-tests)
- - [الگوی Functional Options](#الگوی-functional-options)
-- [بررسی و تمیز کردن (linting)](#بررسی-و-تمیز-کردن-linting)
-
-## مقدمه
-
-استایلها، قراردادهایی هستند که کد ما را کنترل میکنند. شاید کلمه یا اصطلاح استایل درست نباشد، زیرا این قوانین خیلی فراتر از فقط قالببندی فایل منبع هستند - gofmt این کار را برای ما انجام میدهد.
-
-هدف اصلی این راهنما مدیریت پیچیدگیها با توضیح دقیق "بایدها و نبایدهای" نوشتن کد Go در Uber است. این قوانین برای مدیریت راحتتر کد منبع است و همچنین به مهندسان اجازه می دهد تا از ویژگیهای زبان Go به طور موثر استفاده کنند.
-
-این راهنما در اصل توسط [Prashant Varanasi](https://github.com/prashantv) و [Simon Newton](https://github.com/nomis52) به عنوان راهی برای برای آموزش سریع همکاران به استفاده از Go ایجاد شده است. در طول سالها، بر اساس بازخوردهای دیگران، این راهنما تصحیح و بهروزرسانی شده است.
-
-این راهنما اصول و قوانین معمولی در نوشتن کد Go در Uber را شامل میشود. بسیاری از این موارد، رهنمودهای عمومی برای Go هستند، در حالی که برخی از آنها از منابع خارجی نشات می گیرند:
-
-1. [Effective Go](https://golang.org/doc/effective_go.html)
-2. [Go Common Mistakes](https://github.com/golang/go/wiki/CommonMistakes)
-3. [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments)
-
-هدف ما این است که نمونه کدهای ما برای آخرین نسخههای اخیر منتشر شده Go [releases](https://go.dev/doc/devel/release) تنظیم شوند.
-
-همه کدها هنگام اجرا با استفاده از `golint` و `go vet` باید بدون خطا باشند. ما پیشنهاد میکنیم ویرایشگر خود را بهصورت زیر تنظیم کنید:
-
-- Run `goimports` on save
-- Run `golint` and `go vet` to check for errors
-
-میتوانید اطلاعات مربوط به پشتیبانی ویرایشگر برای ابزارهای Go را در اینجا پیدا کنید:
-https://github.com/golang/go/wiki/IDEsAndTextEditorPlugins
-
-## راهنماها
-
-### ارجاع به رابط ها _(Pointers to Interfaces)_
-
-تقریباً هرگز نیازی به داشتن یک اشارهگر (pointer) به یک رابط (interface) ندارید. شما باید رابطها را به عنوان مقادیر(passing
-interfaces as values) ارسال کنید - دیتاهای زیرین (underlying data) میتوانند اشارهگر باشند.
-
-یک رابط (interface) دارای دو فیلد است:
-
-1. یک اشارهگر (pointer) به برخی از اطلاعات یک نوع خاص که شما میتوانید آن را به عنوان یک "type" در نظر بگیرید.
-2. اشارهگر داده. اگر داده ذخیره شده یک اشارهگر باشد، به صورت مستقیم ذخیره میشود. اگر داده ذخیره شده یک مقدار باشد، آنگاه یک اشارهگر به مقدار ذخیره شده میشود.
-
-اگر میخواهید متدهای رابط(interface)، تغییراتی روی داده زیرین اعمال کنند، باید از یک اشارهگر استفاده کنید.
-
-### انطباق پذیری رابط ها
-
-در صورت لزوم، مطابقت رابط را در زمان کامپایل بررسی کنید. این شامل:
-
-- تایپهای Exported که برای پیاده سازی رابطهای خاص به عنوان بخشی از قرارداد API مورد نیاز هستند
-- تایپهای Exported یا unexported که بخشی از مجموعهای از تایپها هستند که همگی یک رابط مشابهی را پیادهسازی میکنند
-- سایر موارد دیگری که در آن نقض یک رابط باعث نقض قراردادها میشود
-
-
-بد | خوب |
-
-
-
-```go
-type Handler struct {
- // ...
-}
-
-
-
-func (h *Handler) ServeHTTP(
- w http.ResponseWriter,
- r *http.Request,
-) {
- // ...
-}
-```
-
- |
-
-```go
-type Handler struct {
- // ...
-}
-
-var _ http.Handler = (*Handler)(nil)
-
-func (h *Handler) ServeHTTP(
- w http.ResponseWriter,
- r *http.Request,
-) {
- // ...
-}
-```
-
- |
-
-
-دستور `var _ http.Handler = (*Handler)(nil)` در صورت عدم تطبیق `*Handler` با رابط `http.Handler`، کامپایل نخواهد شد.
-
-سمت راست تخصیص داده شده عبارت بالا باید مقدارصفر (zero value) نوع ادعا شده باشد. این مقدار برای انواع اشارهگر (مانند Handler\*)، آرایهها و نقشهها `nil` و برای انواع ساختاری (struct) یک ساختار خالی (empty struct) است.
-
-```go
-type LogHandler struct {
- h http.Handler
- log *zap.Logger
-}
-
-var _ http.Handler = LogHandler{}
-
-func (h LogHandler) ServeHTTP(
- w http.ResponseWriter,
- r *http.Request,
-) {
- // ...
-}
-```
-
-### گیرنده ها و رابط ها _(Recievers and Interfaces)_
-
-متدهایی که دارای گیرندههای مقدار (value receivers) هستند، میتوانند روی اشارهگرها و همچنین مقادیر فراخوانی شوند. متدهایی که دارای گیرندههای اشارهگر (pointer receivers) هستند، فقط میتوانند روی اشارهگرها یا مقادیر آدرسپذیر [addressable values](https://golang.org/ref/spec#Method_values) فراخوانی شوند.
-
-برای مثال,
-
-```go
-type S struct {
- data string
-}
-
-func (s S) Read() string {
- return s.data
-}
-
-func (s *S) Write(str string) {
- s.data = str
-}
-
-// ما میتوانیم متد Read را بر روی مقادیری که در نقشهها ذخیره شدهاند، فراخوانی کنیم
-// زیرا متد Read دارای گیرنده مقدار (value receiver) است و این نیازی به قابل دسترس بودن مقدار ندارد
-sVals := map[int]S{1: {"A"}}
-
-
-sVals[1].Read()
-
-// ما نمیتوانیم متد Write را بر روی مقادیری که در نقشهها ذخیره شدهاند، فراخوانی کنیم
-// زیرا متد Write دارای گیرنده اشارهگر (pointer receiver) است
-// و امکان دسترسی به مقادیری که در نقشه ذخیره شده است، با اشارهگر وجود ندارد
-//
-// sVals[1].Write("test")
-
-sPtrs := map[int]*S{1: {"A"}}
-
-// اگر نقشه اشارهگرها را در خود ذخیره کند، شما میتوانید هم متد Read و هم متد Write را فراخوانی کنید،
-// زیرا اشارهگرها به طور طبیعی آدرسپذیر هستند
-sPtrs[1].Read()
-sPtrs[1].Write("test")
-```
-
-به طور مشابه، یک رابط می تواند توسط یک اشارهگر satisfy شود، حتی اگر متد دارای یک گیرنده مقدار (value receiver) باشد.
-
-```go
-type F interface {
- f()
-}
-
-type S1 struct{}
-
-func (s S1) f() {}
-
-type S2 struct{}
-
-func (s *S2) f() {}
-
-s1Val := S1{}
-s1Ptr := &S1{}
-s2Val := S2{}
-s2Ptr := &S2{}
-
-var i F
-i = s1Val
-i = s1Ptr
-i = s2Ptr
-
-// موارد زیر کامپایل نمی شوند، زیرا s2Val یک مقدار است و هیچ گیرنده مقداری برای f وجود ندارد.
-// i = s2Val
-```
-
-منبع Effective Go توضیح بسیار خوبی در مورد [Pointers vs. Values](https://golang.org/doc/effective_go.html#pointers_vs_values) دارد.
-
-### مقدار صفر (zero-value) Mutexها معتبر هستند
-
-مقدار صفر `sync.Mutex` و `sync.RWMutex` معتبر است، بنابراین تقریباً هرگز نیازی به اشارهگر به mutex ندارید.
-
-
-بد | خوب |
-
-
-
-```go
-mu := new(sync.Mutex)
-mu.Lock()
-```
-
- |
-
-```go
-var mu sync.Mutex
-mu.Lock()
-```
-
- |
-
-
-اگر از اشارهگر به یک ساختار (struct) استفاده میکنید، mutex باید به عنوان یک فیلد غیر اشارهگری درون آن قرار گیرد. حتی اگر ساختار (struct) به صورت (non-exported) استفاده شود، نباید mutex را به طور مستقیم درون ساختار جاسازی (embedded) کنید.
-
-
-بد | خوب |
-
-
-
-```go
-type SMap struct {
- sync.Mutex
-
- data map[string]string
-}
-
-func NewSMap() *SMap {
- return &SMap{
- data: make(map[string]string),
- }
-}
-
-func (m *SMap) Get(k string) string {
- m.Lock()
- defer m.Unlock()
-
- return m.data[k]
-}
-```
-
- |
-
-```go
-type SMap struct {
- mu sync.Mutex
-
- data map[string]string
-}
-
-func NewSMap() *SMap {
- return &SMap{
- data: make(map[string]string),
- }
-}
-
-func (m *SMap) Get(k string) string {
- m.mu.Lock()
- defer m.mu.Unlock()
-
- return m.data[k]
-}
-```
-
- |
-
-
-
-فیلد `Mutex` و متدهای `Lock` and `Unlock` ناخواسته بخشی از API صادر شده `SMap` هستند.
-
- |
-
-میوتکس (mutex) و متدهای آن جزئیات پیادهسازی `SMap` هستند که از تماسگیرندگان آن مخفی میمانند.
-
- |
-
-
-### کپی کردن بخش های مشخص از Sliceها و Mapها
-
-برشها (slices) و نقشهها (maps) شامل اشارهگرهایی به داده زیرین خود هستند، بنابراین در مواردی که نیاز به کپی آنها دارید، مراقب باشید.
-
-#### دریافت Slices و Maps
-
- به خاطر داشته باشید که اگر شما یک ارجاع به Map یا Slice که به عنوان ورودی دریافت کردهاید نگه دارید، کاربران ممکن است تغییراتی در آنها ایجاد کنند.
-
-
-بد | خوب |
-
-
-
-
-```go
-func (d *Driver) SetTrips(trips []Trip) {
- d.trips = trips
-}
-
-trips := ...
-d1.SetTrips(trips)
-
-// آیا شما منظورتان از تغییر d1.trips بود؟
-trips[0] = ...
-```
-
- |
-
-
-```go
-func (d *Driver) SetTrips(trips []Trip) {
- d.trips = make([]Trip, len(trips))
- copy(d.trips, trips)
-}
-
-trips := ...
-d1.SetTrips(trips)
-
-// ما میتوانیم trips[0] را تغییر دهیم بدون اینکه تأثیری روی d1.trips داشته باشه.
-trips[0] = ...
-```
-
- |
-
-
-
-
-
-#### برگرداندن Slices و Maps
-
-به طور مشابه، مراقب تغییراتی باشید که کاربران در Mapها یا Sliceها اعمال میکنند و وضعیت داخلی آنها را فاش میکنند.
-
-
-بد | خوب |
-
-
-
-```go
-type Stats struct {
- mu sync.Mutex
- counters map[string]int
-}
-
-// "Snapshot" وضعیت فعلی را برمیگرداند
-func (s *Stats) Snapshot() map[string]int {
- s.mu.Lock()
- defer s.mu.Unlock()
-
- return s.counters
-}
-
-// "Snapshot" دیگر توسط mutex محافظت نمیشود
-// بنابراین هر دسترسی به "Snapshot" منجر به احتمال تداخل دادهها (data races) میشود.
-snapshot := stats.Snapshot()
-```
-
- |
-
-```go
-type Stats struct {
- mu sync.Mutex
- counters map[string]int
-}
-
-func (s *Stats) Snapshot() map[string]int {
- s.mu.Lock()
- defer s.mu.Unlock()
-
- result := make(map[string]int, len(s.counters))
- for k, v := range s.counters {
- result[k] = v
- }
- return result
-}
-
-// "Snapshot" اینجا یک کپی است
-snapshot := stats.Snapshot()
-```
-
- |
-
-
-### به تعویق انداختن (Defer) پاکسازی منابع
-
-از defer برای پاکسازی منابعی مانند فایلها و قفلها استفاده کنید.
-
-
-بد | خوب |
-
-
-
-```go
-p.Lock()
-if p.count < 10 {
- p.Unlock()
- return p.count
-}
-
-p.count++
-newCount := p.count
-p.Unlock()
-
-return newCount
-
-// به دلیل وجود return های متعدد
-// ممکن است آزاد کردن قفلها را فراموش کنید
-```
-
- |
-
-```go
-p.Lock()
-defer p.Unlock()
-
-if p.count < 10 {
- return p.count
-}
-
-p.count++
-return p.count
-
-// خیلی خواناتر
-```
-
- |
-
-
-استفاده از `defer` سربار خیلی کمی دارد و فقط در صورتی باید از آن اجتناب کرد که بتوانید اثبات کنید زمان اجرای تابع شما در مرتبه نانوثانیه قرار دارد. از نظر خوانایی کد، استفاده از `defer` ارزشمند است. این موضوع به خصوص برای متدهای بزرگتر که دارای عملیاتهای پیچیدهتری هستند و محاسبات دیگری در آنها مهمتر از `defer` هستند، صدق میکند.
-
-### اندازه کانال (Channel) یک یا هیچ است
-
-کانالها به طور معمول باید دارای اندازه یک یا بدون بافر باشند. به طور پیشفرض، کانالها بدون بافر و با اندازه صفر هستند. هر اندازه دیگری باید با دقت بررسی شود. در نظر داشته باشید که چگونه اندازه تعیین میشود، چه چیزی مانع پر شدن کانال تحت بار میشود و باعث مسدود شدن Writerها میشود و با وجود این اتفاقات چه چیزی رخ میدهد.
-
-
-بد | خوب |
-
-
-
-```go
-// باید برای هر کسی کافی باشد!
-c := make(chan int, 64)
-```
-
- |
-
-```go
-// اندازه یک
-c := make(chan int, 1) // یا
-// کانال بدون بافر، اندازه صفر
-c := make(chan int)
-```
-
- |
-
-
-### ثابت های نام گذاری شده (Enums) را از یک شماره گذاری کنید
-
-روش استاندارد برای معرفی تعداد محدودی (enumeration) در Go، اعلام یک نوع سفارشی (custom type) و یک گروه `const` با `iota` است. از آنجا که متغیرها معمولاً مقدار پیشفرض 0 دارند، بهتر است enums خود را با یک مقدار غیرصفر شروع کنید.
-
-
-بد | خوب |
-
-
-
-```go
-type Operation int
-
-const (
- Add Operation = iota
- Subtract
- Multiply
-)
-
-// Add=0, Subtract=1, Multiply=2
-```
-
- |
-
-```go
-type Operation int
-
-const (
- Add Operation = iota + 1
- Subtract
- Multiply
-)
-
-// Add=1, Subtract=2, Multiply=3
-```
-
- |
-
-
-مواردی وجود دارد که استفاده از مقدار صفر منطقی است، برای مثال زمانی که حالت صفر رفتار پیشفرض مطلوب است.
-
-```go
-type LogOutput int
-
-const (
- LogToStdout LogOutput = iota
- LogToFile
- LogToRemote
-)
-
-// LogToStdout=0, LogToFile=1, LogToRemote=2
-```
-
-
-
-### استفاده از `"Time"` برای مدیریت زمان
-
-زمان پیچیده است. مفروضات نادرستی که اغلب در مورد زمان انجام میشود شامل موارد زیر است.
-
-1. یک روز 24 ساعت دارد
-2. یک ساعت 60 دقیقه دارد
-3. یک هفته 7 روز دارد
-4. یک سال 365 روز دارد
-5. [و موارد دیگر](https://infiniteundo.com/post/25326999628/falsehoods-programmers-believe-about-time)
-
-به عنوان مثال، _1_ به این معنی است که افزودن 24 ساعت به یک لحظه از زمان، همیشه یک روز تقویمی جدید ایجاد نمیکند.
-
-بنابراین، هنگام برخورد با زمان، همیشه از پکیج [`"زمان"`](https://golang.org/pkg/time/) استفاده کنید زیرا به مقابله با این فرضیات نادرست به شیوه ای مطمئنتر و دقیقتر کمک میکند.
-
-#### از `time.Time` برای نمایش لحظات زمانی استفاده کنید.
-
-هنگام کار با لحظات زمانی از نوع [`time.Time`](https://golang.org/pkg/time/#Time) و متدهای مربوط به `time.Time` برای مقایسه، افزودن، یا کاستن زمان استفاده کنید.
-
-
-بد | خوب |
-
-
-
-```go
-func isActive(now, start, stop int) bool {
- return start <= now && now < stop
-}
-```
-
- |
-
-```go
-func isActive(now, start, stop time.Time) bool {
- return (start.Before(now) || start.Equal(now)) && now.Before(stop)
-}
-```
-
- |
-
-
-#### از `time.Duration` برای بازههای زمانی استفاده کنید.
-
-هنگام کار با بازههای زمانی از نوع [`time.Duration`](https://golang.org/pkg/time/#Duration) استفاده کنید.
-
-
-بد | خوب |
-
-
-
-```go
-func poll(delay int) {
- for {
- // ...
- time.Sleep(time.Duration(delay) * time.Millisecond)
- }
-}
-
-poll(10) // ثانیه بود یا میلیثانیه؟
-```
-
- |
-
-```go
-func poll(delay time.Duration) {
- for {
- // ...
- time.Sleep(delay)
- }
-}
-
-poll(10*time.Second)
-```
-
- |
-
-
-به مثال افزودن 24 ساعت به یک لحظه زمانی برگردیم، روشی که برای اضافه کردن زمان استفاده میکنیم به هدف ما بستگی دارد. اگر بخواهیم همان نقطه زمانی را در روز بعدی تقویم (روز بعد از روز جاری) داشته باشیم، باید از [`Time.AddDate`](https://golang.org/pkg/time/#Time.AddDate) استفاده کنیم. اما اگر بخواهیم یک لحظه زمانی داشته باشیم که تضمین میکند 24 ساعت بعد از زمان قبلی باشد، باید از متد [`Time.Add`](https://golang.org/pkg/time/#Time.Add) استفاده کنیم.
-
-```go
-newDay := t.AddDate(0 /* سالها */, 0 /* ماهها */, 1 /* روزها */)
-maybeNewDay := t.Add(24 * time.Hour)
-```
-
-#### در تعامل با سیستمهای خارجی، از نوعهای `time.Time` و `time.Duration` استفاده کنید.
-
-در صورت امکان در تعاملات با سیستمهای خارجی، از نوعهای `time.Duration` و `time.Time` استفاده کنید. به عنوان مثال:
-
- - در پردازش پارامترهای خط فرمان (Command-line flags)، کتابخانه [`flag`](https://golang.org/pkg/flag/) توانایی پشتیبانی از نوع `time.Duration` را از طریق تابع [`time.ParseDuration`](https://golang.org/pkg/time/#ParseDuration) دارد.
-- در پردازش دادههای JSON، کتابخانه [`encoding/json`](https://golang.org/pkg/encoding/json/) از تبدیل نوع `time.Time` به یک رشته [RFC 3339](https://tools.ietf.org/html/rfc3339) به وسیله تابع [`UnmarshalJSON` method](https://golang.org/pkg/time/#Time.UnmarshalJSON) پشتیبانی میکند.
-- در پردازش دادههای SQL، کتابخانه [`database/sql`](https://golang.org/pkg/database/sql/) توانایی تبدیل ستونهای DATETIME یا TIMESTAMP به نوع `time.Time` و برعکس را دارد، اگر درایور پایگاه داده مربوط این پشتیبانی را داشته باشد.
-- در پردازش دادههای YAML، کتابخانه [`gopkg.in/yaml.v2`](https://godoc.org/gopkg.in/yaml.v2) از نوع `time.Time` به عنوان یک رشته [RFC 3339](https://tools.ietf.org/html/rfc3339) و از تابع [`time.ParseDuration`](https://golang.org/pkg/time/#ParseDuration) برای پشتیبانی از نوع `time.Duration` استفاده میکند.
-
-زمانی که در تعامل با سیستمهای خارجی امکان استفاده از نوع `time.Duration` وجود ندارد، میتوانید از انواع داده مانند `int` یا `float64` استفاده کنید و واحد زمان را در نام فیلد درج کنید.
-
-برای مثال، از آنجایی که `encoding/json` از `time.Duration` پشتیبانی نمی کند، واحد زمان در نام فیلد گنجانده شده است.
-
-
-بد | خوب |
-
-
-
-```go
-// {"interval": 2}
-type Config struct {
- Interval int `json:"interval"`
-}
-```
-
- |
-
-```go
-// {"intervalMillis": 2000}
-type Config struct {
- IntervalMillis int `json:"intervalMillis"`
-}
-```
-
- |
-
-
-زمانی که در تعامل با سیستمهای خارجی امکان استفاده از نوع `time.Time` وجود نداشته باشد، از نوع `string` استفاده کنید و زمانها را با فرمت مشخص شده در [RFC 3339](https://tools.ietf.org/html/rfc3339) تعریف کنید مگر اینکه روش جایگزین دیگری داشته باشید. این فرمت به طور پیشفرض توسط تابع [`Time.UnmarshalText`](https://golang.org/pkg/time/#Time.UnmarshalText) استفاده میشود و از طریق [`time.RFC3339`](https://golang.org/pkg/time/#RFC3339) در توابع `Time.Format` و `time.Parse` نیز در دسترس هستند.
-
-اگرچه این معمولاً مشکلی ایجاد نمیکند، به یاد داشته باشید که پکیج `"time"` از Go قادر به parse کردن زمانهایی با ثانیههای کبیسه (leap seconds) را ندارد ([8728](https://github.com/golang/go/issues/8728)) و همچنین در محاسبات، ثانیههای کبیسه را در نظر نمیگیرد ([15190](https://github.com/golang/go/issues/15190)). اگر دو لحظه زمانی را مقایسه کنید، اختلاف زمانی شامل ثانیههای کبیسه که ممکن است بین این دو لحظه رخ داده باشد، نخواهد بود.
-
-### خطاها (Errors)
-
-#### انواع خطاها
-
-گزینههای کمی برای اعلام خطا وجود دارد. قبل از انتخاب گزینهای که مناسبترین مورد استفاده شما است، موارد زیر را در نظر بگیرید:
-
-- آیا تماس گیرنده (caller) باید خطا را مطابقت دهد تا بتواند آن را مدیریت کند؟ اگر چنین است، باید از توابع [`errors.Is`](https://golang.org/pkg/errors/#Is) یا [`errors.As`](https://golang.org/pkg/errors/#As) با اعلان متغیرهای خطای سطح بالا یا انواع سفارشی پشتیبانی کنیم.
-- آیا پیام خطا یک رشته ثابت است یا یک رشته پویا است که به اطلاعات متنی نیاز دارد؟ در مورد رشتههای استاتیک میتوانیم از [`errors.New`](https://golang.org/pkg/errors/#New) استفاده کنیم، اما برای دومی باید از [`fmt.Errorf`](https://golang.org/pkg/fmt/#Errorf) یا یک نوع خطای سفارشی استفاده کنیم.
-- آیا ما خطای جدیدی را منتشر میکنیم که توسط توابع پایین دست بازگردانده شده است؟ اگر چنین است، بخش [section on error wrapping](#error-wrapping) را ببینید.
-
-| خطا مطابقت دارد؟ | پیغام خطا | راهنمایی |
-| --------------- | ------------- | ----------------------------------------------------------------------- |
-| No | static | [`errors.New`](https://golang.org/pkg/errors/#New) |
-| No | dynamic | [`fmt.Errorf`](https://golang.org/pkg/fmt/#Errorf) |
-| Yes | static | top-level `var` with [`errors.New`](https://golang.org/pkg/errors/#New) |
-| Yes | dynamic | custom `error` type |
-
-به عنوان مثال، از [`errors.New`](https://golang.org/pkg/errors/#New) برای نمایش خطاها با رشته ایستا (static string) استفاده کنید. اگر تماس گیرنده (caller) نیاز به مطابقت و رسیدگی به این خطا دارد، این خطا را به عنوان یک متغیر برای پشتیبانی از تطبیق آن با errors.Is صادر کنید.
-
-
-بدون تطابق خطا | تطابق خطا |
-
-
-
-```go
-// package foo
-
-func Open() error {
- return errors.New("could not open")
-}
-
-// package bar
-
-if err := foo.Open(); err != nil {
- // Can't handle the error.
- panic("unknown error")
-}
-```
-
- |
-
-```go
-// package foo
-
-var ErrCouldNotOpen = errors.New("could not open")
-
-func Open() error {
- return ErrCouldNotOpen
-}
-
-// package bar
-
-if err := foo.Open(); err != nil {
- if errors.Is(err, foo.ErrCouldNotOpen) {
- // handle the error
- } else {
- panic("unknown error")
- }
-}
-```
-
- |
-
-
-برای خطای با رشته پویا (dynamic string)، اگر تماس گیرنده نیازی به تطبیق آن نداشته باشد، از [`fmt.Errorf`](https://golang.org/pkg/fmt/#Errorf) و اگر تماس گیرنده نیاز به تطبیق آن داشته باشد، از یک خطای سفارشی استفاده کنید.
-
-
-بدون تطابق خطا | تطابق خطا |
-
-
-
-```go
-// package foo
-
-func Open(file string) error {
- return fmt.Errorf("file %q not found", file)
-}
-
-// package bar
-
-if err := foo.Open("testfile.txt"); err != nil {
- // Can't handle the error.
- panic("unknown error")
-}
-```
-
- |
-
-```go
-// package foo
-
-type NotFoundError struct {
- File string
-}
-
-func (e *NotFoundError) Error() string {
- return fmt.Sprintf("file %q not found", e.File)
-}
-
-func Open(file string) error {
- return &NotFoundError{File: file}
-}
-
-
-// package bar
-
-if err := foo.Open("testfile.txt"); err != nil {
- var notFound *NotFoundError
- if errors.As(err, ¬Found) {
- // handle the error
- } else {
- panic("unknown error")
- }
-}
-```
-
- |
-
-
-توجه داشته باشید که اگر متغیرها یا انواع خطا را از یک پکیج (package) صادر کنید، آنها بخشی از API عمومی پکیج خواهند شد.
-
-#### بسته بندی خطا (Error Wrapping)
-
-در صورت عدم موفقیت یک فراخوانی (call)، سه گزینه اصلی برای انتشار خطا وجود دارد:
-
-- خطای اصلی را همانطور که هست برگردانید
-- زمینه (context) را با `fmt.Errorf` و فعل `%w` اضافه کنید
-- زمینه (context) را با `fmt.Errorf` و فعل `%v` اضافه کنید
-
-اگر زمینه (context) اضافی برای افزودن وجود ندارد، خطای اصلی را همانطور که هست برگردانید. این، نوع خطا و پیام اصلی را حفظ میکند و برای مواردی که پیام خطای اصلی اطلاعات کافی برای ردیابی اینکه خطا از کجا آمده است، مناسب است.
-
-در غیر این صورت، در صورت امکان، زمینه (context) را به پیام خطا اضافه کنید تا به جای دریافت خطاهای مبهم مانند "اتصال رد شد" ("connection refused")، خطاهای مفیدتری مانند "تماس با سرویس foo: اتصال رد شد" ("call service foo: connection refused") دریافت کنید.
-
-از `fmt.Errorf` برای افزودن زمینه (context) به خطاهای خود استفاده کنید، بسته به اینکه تماس گیرنده بتواند علت اصلی را مطابقت داده و استخراج کند، بین `%w` یا `%v` افعال را انتخاب کنید.
-
- - اگر تماسگیرنده (caller) لازم است که به خطای اصلی دسترسی داشته باشه، از `%w` استفاده کنید. این یک پیش فرض خوب برای اکثر خطاهای بسته بندی است، اما توجه داشته باشید که تماس گیرندگان ممکن است به این رفتار تکیه کنند. بنابراین برای مواردی که خطای wrapping یک var یا نوع شناخته شده است، آن را مستند کنید و آن را به عنوان بخشی از قرارداد تابع آزمایش کنید.
- - از `%v` برای مبهم کردن خطای اصلی استفاده کنید. تماسگیرنده نمیتواند با آن مطابقت کند، اما در صورت نیاز میتوانید در آینده به `%w` تغییر دهید.
-
-هنگام اضافه کردن توضیحات به خطاهای برگشتی، با اجتناب از عباراتی مانند "failed to"، که لایه به لایه جمع میشود، (منظور اینکه هنگامی که یک خطا از سطح پایینتری به سطح بالاتری در سلسلهمراتب کد حرکت میکند، تعداد خطاها و اطلاعات اضافی که به آن افزوده میشوند، افزایش مییابد و سنگینتر میشود) متن context را مختصر نگه دارید:
-
-
-بد | خوب |
-
-
-
-```go
-s, err := store.New()
-if err != nil {
- return fmt.Errorf(
- "failed to create new store: %w", err)
-}
-```
-
- |
-
-```go
-s, err := store.New()
-if err != nil {
- return fmt.Errorf(
- "new store: %w", err)
-}
-```
-
- |
-
-```plain
-failed to x: failed to y: failed to create new store: the error
-```
-
- |
-
-```plain
-x: y: new store: the error
-```
-
- |
-
-
-با این حال، هنگامی که خطا به سیستم دیگری ارسال میشود، باید مشخص باشد که پیام یک خطا است (به عنوان مثال یک برچسب `err` یا پیشوند "ناموفق" در لاگها).
-
-همچنین ببینید: [فقط خطاها را بررسی نکنید، آنها را با ظرافت مدیریت کنید](https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully).
-
-#### نام گذاری خطا
-
-برای مقادیر خطا که به عنوان متغیرهای سراسری ذخیره میشوند بسته به اینکه آیا آنها صادر شده (exported) هستند یا خیر، از پیشوند `Err` یا `err` استفاده کنید. این راهنما جایگزین قاعده [از پیشوند `"_"` برای متغیرهای خصوصی (Unexported) استفاده کنید](#از-پیشوند-_-برای-متغیرهای-خصوصی-unexported-استفاده-کنید) است.
-
-```go
-var (
- // در زیر، دو خطای زیر به صورت صادرشده (exported) است تا کاربران این بسته بتوانند آنها را با استفاده از `errors.Is` مطابقت دهند.
-
- ErrBrokenLink = errors.New("link is broken")
- ErrCouldNotOpen = errors.New("could not open")
-
- // این خطا صادر نمی شود زیرا نمیخواهیم بخشی از API عمومی ما باشد. ممکن است همچنان از آن در داخل یک بسته با اشکال استفاده کنیم.
-
- errNotFound = errors.New("not found")
-)
-```
-
-برای نوعهای سفارشی خطا، از پسوند `Error` استفاده کنید.
-
-```go
-
-// به همین ترتیب، این خطا صادر می شود تا کاربران این بسته بتوانند آن را با errors.As مطابقت دهند.
-
-type NotFoundError struct {
- File string
-}
-
-func (e *NotFoundError) Error() string {
- return fmt.Sprintf("file %q not found", e.File)
-}
-
-// و این خطا صادر نمی شود زیرا ما نمی خواهیم آن را بخشی از API عمومی کنیم.
-// ما هنوز هم امکان استفاده از آن را داخل پکیج با errors.As داریم.
-
-type resolveError struct {
- Path string
-}
-
-func (e *resolveError) Error() string {
- return fmt.Sprintf("resolve %q", e.Path)
-}
-```
-
-#### مدیریت یکباره خطاها
-
-وقتی فراخواننده یک خطا از فراخواننده دیگری دریافت میکند، بسته به اطلاعاتی که در مورد خطا دارد، میتواند آن را به روشهای مختلفی اداره کند.
-
-این شامل موارد زیر است اما محدود به این موارد نیستند:
-
-- اگر قرارداد فراخواننده خطاهای مشخصی تعریف کرده باشد، میتوان با استفاده از `errors.Is` یا `errors.As` تطابق خطا را انجام داد و با توجه به اطلاعات موجود، درخواستها را به صورت متفاوت اداره کرد.
-- اگر خطا قابل بازیابی باشد، خطا را ثبت کرده و سپس به تدریج به حالت نرمال بازگردید.
-- اگر خطا وضعیت شکست مرتبط با دامنه خاصی را نمایان میکند، یک خطای دقیقاً تعریف شده را بازگردانید.
-- خطا را بازگردانید، ساده (verbatim) یا به صورت پیچیده [wrapped](#error-wrapping)، به توجه به شرایط.
-
-صرف نظر از نحوه برخورد تماس گیرنده با خطاها، معمولاً باید هر خطا را فقط یک بار مدیریت کند. به عنوان مثال، تماس گیرنده نباید خطا را ثبت کند و سپس آن را برگرداند، زیرا تماس گیرنده آن نیز ممکن است خطا را مدیریت کند.
-
-به عنوان مثال موارد زیر را در نظر بگیرید:
-
-
-توضیحات | کد |
-
-
-
-**بد**: خطا را ثبت کنید و آن را برگردانید
-
-تماس گیرندگان در پشته ممکن است اقدامات مشابهی در مورد این خطا انجام دهند. انجام این کار باعث تولید مقدار زیادی اطلاعات ناکارآمد در گزارشهای برنامه میشود که ارزش چندانی نخواهد داشت.
-
- |
-
-```go
-u, err := getUser(id)
-if err != nil {
- // BAD: See description
- log.Printf("Could not get user %q: %v", id, err)
- return err
-}
-```
-
- |
-
-
-**خوب**: خطا را Wrap کنید و برگردانید.
-
-تماس گیرندگان بالاتر از پشته، خطا را کنترل خواهند کرد.
-استفاده از `%w` تضمین میکند که میتوانند خطا را با `errors.Is` یا `errors.As` مطابقت دهند.
-
- |
-
-```go
-u, err := getUser(id)
-if err != nil {
- return fmt.Errorf("get user %q: %w", id, err)
-}
-```
-
- |
-
-
-**خوب**: ابتدا خطا را ثبت کنید (لاگ کنید) و سپس به آرامی و به صورت کنترل شده به وضعیت عادی یا نرمال خود بازگردید
-
-اگر یک عملیات خاصی در برنامه نیاز به اجرا ندارد و میتواند به صورت کمکیفیتتری انجام شود، میتوانیم از ابزارها و راهکارهایی استفاده کنیم تا از خطاها بازیابی کنیم و تجربه کاربران را بدون وقوع شکست بهبود بخشیم.
-
- |
-
-```go
-if err := emitMetrics(); err != nil {
- // Failure to write metrics should not
- // break the application.
- log.Printf("Could not emit metrics: %v", err)
-}
-
-```
-
- |
-
-
-**خوب**: ابتدا خطا را تشخیص دهید (تطابق دهید) و سپس به آرامی و به صورت کنترل شده به وضعیت عادی یا نرمال خود بازگردید
-
-اگر فراخواننده (caller) در قرارداد خود یک خطای خاص تعریف کرده باشد و خرابی قابل بازیابی باشد، در مورد آن خطا تطابق (match) کنید و به صورت کنترل شده آن را به حالت عادی بازگردانید. برای موارد دیگر، خطا را پوشش دهید (wrap) و آن را بازگردانید.
-
-سایر خطاها توسط تماس گیرندگان بالاتر در پشته رسیدگی میشود.
-
- |
-
-```go
-tz, err := getUserTimeZone(id)
-if err != nil {
- if errors.Is(err, ErrUserNotFound) {
- // User doesn't exist. Use UTC.
- tz = time.UTC
- } else {
- return fmt.Errorf("get user %q: %w", id, err)
- }
-}
-```
-
- |
-
-
-### مدیریت نوع ادعای (Type Assertion) شکست ها
-
-مقدار برگشتی بدست آمده از [type assertion](https://golang.org/ref/spec#Type_assertions) روی یک تایپ نادرست panic خواهد شد. بنابراین همیشه از اصطلاح "comma ok" استفاده کنید.
-
-
-بد | خوب |
-
-
-
-```go
-t := i.(string)
-```
-
- |
-
-```go
-t, ok := i.(string)
-if !ok {
- // به خوبی خطا را مدیریت کنید
-}
-```
-
- |
-
-
-
-
-### از ایجاد Panic جلوگیری کنید (Don't Panic)
-
-کدهایی که در محیط تولید (Production) اجرا میشوند، باید از وقوع (Panic) جلوگیری کنند. panicها عامل اصلی ایجاد [شکستهای متوالی(آبشاری)](https://en.wikipedia.org/wiki/Cascading_failure) هستند. اگر خطایی رخ دهد، تابع باید یک خطای مناسب را برگردانده و به فراخواننده (caller) اجازه دهد تا تصمیم بگیرد که چگونه با آن برخورد کند.
-
-
-بد | خوب |
-
-
-
-```go
-func run(args []string) {
- if len(args) == 0 {
- panic("an argument is required")
- }
- // ...
-}
-
-func main() {
- run(os.Args[1:])
-}
-```
-
- |
-
-```go
-func run(args []string) error {
- if len(args) == 0 {
- return errors.New("an argument is required")
- }
- // ...
- return nil
-}
-
-func main() {
- if err := run(os.Args[1:]); err != nil {
- fmt.Fprintln(os.Stderr, err)
- os.Exit(1)
- }
-}
-```
-
- |
-
-
-استفاده از panic/recover به عنوان یک استراتژی مدیریت خطا مناسب نمیباشد. یک برنامه فقط زمانی باید panic کند که چیزی غیرقابل بازیابی اتفاق بیفتد (مثلاً nil dereference). یک استثنا، مقداردهی اولیه برنامه است: شرایط نامطلوبی که باعث می شود برنامه در هنگام شروع به کار متوقف شود، ممکن است باعث panic شود.
-
-```go
-var _statusTemplate = template.Must(template.New("name").Parse("_statusHTML"))
-```
-حتی در تستها، `t.Fatal` یا `t.FailNow` را به panics ترجیح دهید تا مطمئن شوید که آزمون بهعنوان ناموفق علامتگذاری شده است.
-
-
-بد | خوب |
-
-
-
-```go
-// func TestFoo(t *testing.T)
-
-f, err := os.CreateTemp("", "test")
-if err != nil {
- panic("failed to set up test")
-}
-```
-
- |
-
-```go
-// func TestFoo(t *testing.T)
-
-f, err := os.CreateTemp("", "test")
-if err != nil {
- t.Fatal("failed to set up test")
-}
-```
-
- |
-
-
-### از پکیج `go.uber.org/atomic` استفاده کنید
-
-از عملیات اتمی بسته [sync/atomic](https://golang.org/pkg/sync/atomic/) برای کار بر روی انواع اولیه (`int32`, `int64` و غیره.) استفاده کنید، بنابراین، ممکن است این نکته از یاد برود که برای دسترسی یا تغییر متغیرها، باید از عملیاتهای اتمیک استفاده کرد.
-
-بسته [go.uber.org/atomic](https://godoc.org/go.uber.org/atomic) ایمنی نوع را با پنهان کردن نوع زیرین به این عملیاتها اضافه میکند. به علاوه، این بسته شامل یک تایپ `atomic.Bool` نیز میشود.
-
-
-بد | خوب |
-
-
-
-```go
-type foo struct {
- running int32 // atomic
-}
-
-func (f* foo) start() {
- if atomic.SwapInt32(&f.running, 1) == 1 {
- // already running…
- return
- }
- // start the Foo
-}
-
-func (f *foo) isRunning() bool {
- return f.running == 1 // race!
-}
-```
-
- |
-
-```go
-type foo struct {
- running atomic.Bool
-}
-
-func (f *foo) start() {
- if f.running.Swap(true) {
- // already running…
- return
- }
- // start the Foo
-}
-
-func (f *foo) isRunning() bool {
- return f.running.Load()
-}
-```
-
- |
-
-
-### از متغیرهای سراسری تغییرپذیر (Mutable Globals) خودداری کنید
-
-از تزریق وابستگی (Dependency Injection) بجای تغییر متغیرهای سراسری استفاده کنید. این مورد روی اشارهگرهای تابع (function pointers) و همچنین برای انواع مقادیر دیگر نیز اعمال میشود.
-
-
-بد | خوب |
-
-
-
-```go
-// sign.go
-
-var _timeNow = time.Now
-
-func sign(msg string) string {
- now := _timeNow()
- return signWithTime(msg, now)
-}
-```
-
- |
-
-```go
-// sign.go
-
-type signer struct {
- now func() time.Time
-}
-
-func newSigner() *signer {
- return &signer{
- now: time.Now,
- }
-}
-
-func (s *signer) Sign(msg string) string {
- now := s.now()
- return signWithTime(msg, now)
-}
-```
-
- |
-
-
-```go
-// sign_test.go
-
-func TestSign(t *testing.T) {
- oldTimeNow := _timeNow
- _timeNow = func() time.Time {
- return someFixedTime
- }
- defer func() { _timeNow = oldTimeNow }()
-
- assert.Equal(t, want, sign(give))
-}
-```
-
- |
-
-```go
-// sign_test.go
-
-func TestSigner(t *testing.T) {
- s := newSigner()
- s.now = func() time.Time {
- return someFixedTime
- }
-
- assert.Equal(t, want, s.Sign(give))
-}
-```
-
- |
-
-
-### از جاسازی نوع ها (Embedding Types) در ساختارهای عمومی خودداری کنید
-
-نوعهای جاسازی شده (embedded types) جزئیات اطلاعات پیادهسازی را فاش میکنند، توسعه تایپ را دشوارتر میکنند و از وضوح مستندات میکاهند.
-
-فرض کنید شما انواع مختلفی از لیستها را با استفاده از یک `AbstractList` مشترک پیادهسازی کردهاید. از تعبیه کردن (embedding) `AbstractList` در پیادهسازیهای خاص لیستهای خود پرهیز کنید. به جای آن، تنها متدهایی را به صورت دستی برای لیست خاص خود ایجاد کنید که به `AbstractList` ارجاع میدهند.
-
-```go
-type AbstractList struct {}
-
-// Add یک موجودیت را به لیست اضافه می کند.
-func (l *AbstractList) Add(e Entity) {
- // ...
-}
-
-// Remove یک موجودیت را از لیست حذف می کند.
-func (l *AbstractList) Remove(e Entity) {
- // ...
-}
-```
-
-
-بد | خوب |
-
-
-
-```go
-// ConcreteList لیستی از موجودیت ها است.
-type ConcreteList struct {
- *AbstractList
-}
-```
-
- |
-
-```go
-// ConcreteList لیستی از موجودیت ها است.
-type ConcreteList struct {
- list *AbstractList
-}
-
-// Add یک موجودیت را به لیست اضافه می کند.
-func (l *ConcreteList) Add(e Entity) {
- l.list.Add(e)
-}
-
-// Remove یک موجودیت را از لیست حذف می کند.
-func (l *ConcreteList) Remove(e Entity) {
- l.list.Remove(e)
-}
-```
-
- |
-
-
-زبان Go امکان [type embedding](https://golang.org/doc/effective_go.html#embedding) را به عنوان یک توافق بین ارثبری و ترکیب فراهم میکند. نوع بیرونی (outer type) نسخههای ضمنی از متدهای نوع تعبیهشده را به طور ضمنی به ارث میبرد و این متدها به طور پیشفرض به متد مشابه در نمونه تعبیهشده ارجاع داده میشوند.
-
-همچنین، ساختار (struct) فیلدی با همان نام نوع تعبیه شده را دریافت می کند. بنابراین، اگر نوع تعبیهشده عمومی باشد، فیلد عمومی است. برای حفظ توانایی کار کردن کدهای قدیمی با نسخههای جدید، هر نسخه بعدی از نوع خارجی باید نوع تعبیه شده را حفظ کند.
-
-نیاز به تعبیه (embedding) نوعها به ندرت پیش میآید. این یک روش مفید است که به شما کمک میکند از نوشتن متدهای دستوری بلند و پیچیده جلوگیری کنید.
-
-حتی اگر یک رابط (_interface_) AbstractList سازگار را جایگزین ساختار (struct) کنید، به توسعهدهنده امکان بیشتری برای تغییر در آینده ارائه میدهد، اما همچنان جزئیات استفاده از یک پیادهسازی انتزاعی برای concrete لیستها را فاش میکند.
-
-
-بد | خوب |
-
-
-
-```go
-// AbstractList یک پیاده سازی کلی از لیست های موجودیت های مختلف است.
-type AbstractList interface {
- Add(Entity)
- Remove(Entity)
-}
-
-// ConcreteList لیستی از موجودیت ها است.
-type ConcreteList struct {
- AbstractList
-}
-```
-
- |
-
-```go
-// ConcreteList لیستی از موجودیت ها است.
-type ConcreteList struct {
- list AbstractList
-}
-
-// Add یک موجودیت را به لیست اضافه می کند.
-func (l *ConcreteList) Add(e Entity) {
- l.list.Add(e)
-}
-
-// Remove یک موجودیت را از لیست حذف می کند.
-func (l *ConcreteList) Remove(e Entity) {
- l.list.Remove(e)
-}
-```
-
- |
-
-
-استفاده از ساختارهای تعبیه شده (embedded struct) یا رابطهای تعبیه شده (embedded interface)، تکامل و توسعه Typeها را محدود می کند.
-
-- افزودن متدها به یک رابط تعبیهشده تغییرات مخربی ایجاد میکند.
-- حذف متدها از یک ساختار تعبیهشده نیز تغییر مخربی محسوب میشود.
-- حذف نوع تعبیهشده همچنین به عنوان یک تغییر مخرب در نظر گرفته میشود.
-- حتی جایگزین کردن نوع تعبیهشده با یک نوع جایگزین که همان رابط مشابه را پیادهسازی میکند، تغییر مخربی به حساب میآید.
-
-اگرچه نوشتن این متدها (متدهای تعبیهشده) کمی زمانبر است، اما تلاش اضافی که برای این کار صرف میشود، باعث میشود جزئیات پیادهسازی متدها پنهان شوند. همچنین، این کار فرصتهای بیشتری برای تغییر در آینده فراهم میکند و همچنین این کار به بهبود پایداری و قابلیت تغییر بیشتر کد کمک میکند و به از بین بردن انحرافات و پیچیدگیهای غیر ضروری در مستندات کمک میکند.
-
-### از استفاده از نام های داخلی (Buit-In) خودداری کنید
-
-[مشخصات زبان](https://golang.org/ref/spec) Go چندین شناسه داخلی و [شناسههای از پیش اعلام شده](https://golang.org/ref/spec#Predeclared_identifiers) را مشخص می کند که نباید در پروژه های Go استفاده شوند.
-
-بسته به زمینه (context)، استفاده مجدد از این شناسهها به عنوان نام، شناسه اصلی را در محدوده فعلی (یا هر محدوده تودرتو) پنهان میکند یا کد را مبهم میکند. در بهترین حالت، کامپایلر یک خطا ایجاد میکند؛ در بدترین حالت، چنین کدی ممکن است خطاهایی را ایجاد کند که بازیابی آنها دشوار است.
-
-
-بد | خوب |
-
-
-
-```go
-var error string
-// استفاده از نام `error` به عنوان یک متغیر یا شناسه، باعث ایجاد سایه روی نام داخلی `error` میشود.
-
-// یا
-
-func handleErrorMessage(error string) {
- // استفاده از نام `error` به عنوان یک متغیر یا شناسه، باعث ایجاد سایه روی نام داخلی `error` میشود.
-}
-```
-
- |
-
-```go
-var errorMessage string
-// در اینجا `error` به عنوان یک متغیر یا شناسه داخلی در نظر گرفته میشود
-
-// or
-
-func handleErrorMessage(msg string) {
- // در اینجا `error` به عنوان یک متغیر یا شناسه داخلی در نظر گرفته میشود
-}
-```
-
- |
-
-
-```go
-type Foo struct {
-// اگرچه این فیلدها از لحاظ فنی سایهزنی (shadowing) را ایجاد نمیکنند، اما جستجوی رشتههای `error` یا `string` در این موارد اکنون مبهم است.
- error error
- string string
-}
-
-func (f Foo) Error() error {
- // `error` و `f.error` از نظر بصری مشابه هم هستند.
- return f.error
-}
-
-func (f Foo) String() string {
- // `string` و `f.string` از نظر بصری مشابه هم هستند.
- return f.string
-}
-```
-
- |
-
-```go
-type Foo struct {
- // `error` و `string` اکنون واضح هستند.
- err error
- str string
-}
-
-func (f Foo) Error() error {
- return f.err
-}
-
-func (f Foo) String() string {
- return f.str
-}
-```
-
- |
-
-
-توجه داشته باشید که کامپایلر هنگام استفاده از شناسههای پیشتعریف شده خطا ایجاد نمی کند، اما ابزارهایی مانند `go vet` به درستی به مشکلات ضمنی در این موارد و موارد دیگر اشاره می کنند.
-
-### از تابع `()init` استفاده نکنید
-
-در صورت امکان، از `()init` پرهیز کنید. وقتی لازم به استفاده از تابع `()init` هستید، کد باید تلاش کند:
-
-1. بدون توجه به محیط برنامه یا نحوه فراخوانی، کاملاً قطعی عمل کند.
-2. سعی کنید از ایجاد وابستگی به ترتیب اجرا یا اثرات جانبی مربوط به توابع `()init` دیگر خودداری کنید.هرچند ترتیب اجرای توابع `()init` به خوبی شناخته شده است، اما کد ممکن است تغییر کند و این وابستگیها میتوانند منجر به ناپایداری و خطاهای پنهان شوند.
-3. از دسترسی یا دستکاری وضعیت سراسری یا محیطی مانند اطلاعات ماشین، متغیرهای محیطی، دایرکتوری کاری، آرگومانها/ورودیهای برنامه و غیره پرهیز کند.
-4. از عملیات ورود/خروج (I/O) مانند عملیات فایلسیستم، شبکه و تماسهای سیستمی پرهیز کند.
-
-کدی که نمیتواند این موارد را انجام دهد، احتمالاً بهتر است به عنوان یک تابع کمکی برای فراخوانی در `()main` (یا در جای دیگر در چرخه عمر برنامه) قرار گیرد یا به عنوان بخشی از خود `()main` نوشته شود. به خصوص کتابخانههایی که برای استفاده در برنامههای دیگر طراحی شدهاند، باید مراقبتهای خاصی را برای تضمین قطعیت کامل داشته باشند و از "جادوی `init`" پرهیز کنند.
-
-
-بد | خوب |
-
-
-
-```go
-type Foo struct {
- // ...
-}
-
-var _defaultFoo Foo
-
-func init() {
- _defaultFoo = Foo{
- // ...
- }
-}
-```
-
- |
-
-```go
-var _defaultFoo = Foo{
- // ...
-}
-
-// یا برای تست پذیری بهتر:
-
-var _defaultFoo = defaultFoo()
-
-func defaultFoo() Foo {
- return Foo{
- // ...
- }
-}
-```
-
- |
-
-
-```go
-type Config struct {
- // ...
-}
-
-var _config Config
-
-func init() {
- // بد: بر اساس دایرکتوری فعلی
- cwd, _ := os.Getwd()
-
- // بد: I/O
- raw, _ := os.ReadFile(
- path.Join(cwd, "config", "config.yaml"),
- )
-
- yaml.Unmarshal(raw, &_config)
-}
-```
-
- |
-
-```go
-type Config struct {
- // ...
-}
-
-func loadConfig() Config {
- cwd, err := os.Getwd()
- // handle err
-
- raw, err := os.ReadFile(
- path.Join(cwd, "config", "config.yaml"),
- )
- // handle err
-
- var config Config
- yaml.Unmarshal(raw, &config)
-
- return config
-}
-```
-
- |
-
-
-با توجه به موارد فوق، شرایطی وجود دارد که ممکن است `()init` ارجح یا ضروری باشد، که ممکن است شامل موارد زیر باشد:
-
-- عبارات پیچیده که نمیتوانند به عنوان انتسابهای تکی نمایان شوند. (مثلا اگر یک متغیر را نمیتوان با یک عبارت ساده از نوع x := value مقداردهی کرد و نیاز به انجام محاسبات پیچیدهتری دارید، در این صورت ممکن است از ()init استفاده کنید.)
-- قلاب های قابل اتصال، مانند پایگاه داده/sql، رجیستری نوع رمزگذاری و غیره.
-- بهینهسازیها برای [Google Cloud توابع](https://cloud.google.com/functions/docs/bestpractices/tips#use_global_variables_to_reuse_objects_in_future_invocations) و سایر اشکال پیش محاسبه قطعی.
-
-### خروج فقط در تابع اصلی (Main)
-
-برنامههای Go برای خروج فوری از برنامه از [`os.Exit`](https://golang.org/pkg/os/#Exit) یا [`log.Fatal*`](https://golang.org/pkg/log/#Fatal) استفاده میکنند. (استفاده از panic به عنوان روشی برای خروج از برنامه مناسب نیست، لطفاً [از panic استفاده نکنید](#dont-panic).)
-
-**تنها در تابع `()main`** از یکی از `os.Exit` یا `log.Fatal*` استفاده کنید. توابع دیگر برای اعلام شکست باید خطاها را به عنوان نتیجه برگردانند.
-
-
-بد | خوب |
-
-
-
-```go
-func main() {
- body := readFile(path)
- fmt.Println(body)
-}
-
-func readFile(path string) string {
- f, err := os.Open(path)
- if err != nil {
- log.Fatal(err)
- }
-
- b, err := io.ReadAll(f)
- if err != nil {
- log.Fatal(err)
- }
-
- return string(b)
-}
-```
-
- |
-
-```go
-func main() {
- body, err := readFile(path)
- if err != nil {
- log.Fatal(err)
- }
- fmt.Println(body)
-}
-
-func readFile(path string) (string, error) {
- f, err := os.Open(path)
- if err != nil {
- return "", err
- }
-
- b, err := io.ReadAll(f)
- if err != nil {
- return "", err
- }
-
- return string(b), nil
-}
-```
-
- |
-
-
-در اصل: برنامههایی که دارای تعدادی تابع هستند که دارای توابع خروج فوری هستند، چندین مسئله را با خود به همراه دارند:
-
-- جریان کنترل ناپیدا: هر تابعی ممکن است باعث خروج برنامه شود، به همین دلیل تبدیل به یک موضوع سخت برای استدلال در مورد کنترل جریان میشود.
-- دشواری در تست: توابعی که برنامه را خارج میکنند باعث میشوند فرآیند تست متوقف شود. این باعث میشود که تست کردن این توابع دشوار شود و منجر به خطر از دست دادن تستهای دیگری که هنوز توسط `go test` اجرا نشدهاند، شود.
-- پاکسازی نادیده گرفته شده: وقتی یک تابع باعث خروج برنامه میشود، اجرای توابعی که با استفاده از عبارتهای `defer` ثبت شدهاند را نادیده میگیرد. این کار باعث افزایش خطر از دست دادن وظایف پایانی مهم میشود.
-
-#### فقط یکبار از یکی از توابع خروج استفاده کنید (Exit Once)
-
-در صورت امکان، **حداکثر یک بار** `os.Exit` یا `log.Fatal` را در تابع `()main` خود فراخوانی کنید. اگر چندین حالت خطا وجود دارد که اجرای برنامه را متوقف میکنند، این منطق را در یک تابع مستقل قرار دهید و از آنجا خطاها را برگردانید.
-
-این کار باعث کوتاه شدن تابع `()main` شما میشود و همه منطق اصلی کسبوکار را در یک تابع مستقل قرار میدهد که قابلیت تست آن را بهبود میدهد.
-
-
-بد | خوب |
-
-
-
-```go
-package main
-
-func main() {
- args := os.Args[1:]
- if len(args) != 1 {
- log.Fatal("missing file")
- }
- name := args[0]
-
- f, err := os.Open(name)
- if err != nil {
- log.Fatal(err)
- }
- defer f.Close()
-
- // اگر بعد از این خط log.Fatal را فراخوانی کنیم، f.Close فراخوانی نمی شود.
-
- b, err := io.ReadAll(f)
- if err != nil {
- log.Fatal(err)
- }
-
- // ...
-}
-```
-
- |
-
-```go
-package main
-
-func main() {
- if err := run(); err != nil {
- log.Fatal(err)
- }
-}
-
-func run() error {
- args := os.Args[1:]
- if len(args) != 1 {
- return errors.New("missing file")
- }
- name := args[0]
-
- f, err := os.Open(name)
- if err != nil {
- return err
- }
- defer f.Close()
-
- b, err := io.ReadAll(f)
- if err != nil {
- return err
- }
-
- // ...
-}
-```
-
- |
-
-
-مثال بالا از `log.Fatal` استفاده می کند، اما این راهنما میتواند برای `os.Exit` یا هر کد کتابخانه ای که `os.Exit` را فراخوانی می کند نیز اعمال می شود.
-
-```go
-func main() {
- if err := run(); err != nil {
- fmt.Fprintln(os.Stderr, err)
- os.Exit(1)
- }
-}
-```
-
-شما میتوانید امضای تابع `()run` را مطابق با نیازهای خود تغییر دهید. به عنوان مثال، اگر برنامه شما باید با کدهای خروج خاصی برای خطاها خارج شود، `()run` میتواند به جای یک خطا، کد خروج را برگرداند. همچنین به تست های واحد اجازه می دهد تا مستقیماً این رفتار را تأیید کنند.
-
-```go
-func main() {
- os.Exit(run(args))
-}
-
-func run() (exitCode int) {
- // ...
-}
-```
-
-لطفاً توجه داشته باشید که تابع `()run` مورد استفاده در این مثالها استفاده شده است اجباری نیست. نام، امضا و تنظیمات تابع `()run` انعطاف پذیر هستند. از جمله موارد دیگر، می توانید:
-
-- آرگومانهای خط فرمان تجزیه نشده (unparsed) را میپذیرد به عنوان مثال (`run(os.Args[1:])`)
-- آرگومانهای خط فرمان را در `()main` تجزیه کنید (parse) و آنها را برای اجرا ارسال کنید
-- با استفاده از تعریف یک تایپ خطای سفارشی، کد خروج را به `()main` برگردانید
-- منطق کسب و کار را در لایههای انتزاعی مختلف `(package main)بسته اصلی` قرار دهید
-
-با این راهنمود، تنها یک مکان در تابع `()main` شما وجود دارد که واقعاً مسئول خروج از پروسه است.
-
-### از برچسب های فیلد در ساختارهای مارشال شده (marshaled) استفاده کنید
-
-هر فیلدی که به فرمتهایی مانند JSON، YAML یا سایر فرمتهایی که از نامگذاری بر اساس تگها پشتیبانی میکنند، باید با تگ مربوطه مشخص شود.
-
-
-بد | خوب |
-
-
-
-```go
-type Stock struct {
- Price int
- Name string
-}
-
-bytes, err := json.Marshal(Stock{
- Price: 137,
- Name: "UBER",
-})
-```
-
- |
-
-```go
-type Stock struct {
- Price int `json:"price"`
- Name string `json:"name"`
- // ایمن برای تغییر نام به نماد.
-}
-
-bytes, err := json.Marshal(Stock{
- Price: 137,
- Name: "UBER",
-})
-```
-
- |
-
-
-گوروتینها سبک هستند، اما رایگان نیستند: حداقل هزینههایی را برای استفاده از حافظه برای پشته آنها و زمان CPU برای زمانبندی آنها دارند. این هزینهها در موارد معمولی کمترین تأثیر را دارند، اما زمانی که تعداد زیادی گوروتین بدون مدیریت صحیح ایجاد میشوند، میتوانند به مشکلات عملکردی بزرگی منجر شوند. همچنین، گوروتینهایی که مدیریت زمانهای چرخه حیات مشخصی ندارند، میتوانند به مشکلات دیگری نیز منجر شوند، مثل جلوگیری از جمعآوری زبالهها (garbage collected) و نگهداشتن منابعی که دیگر استفاده نمیشوند.
-
-### گوروتین ها را به حال خودشان (بدون نظارت) رها نکنید
-
-گوروتینها سبک هستند، اما رایگان نیستند: حداقل هزینههایی را برای استفاده از حافظه برای پشته آنها و زمان CPU برای زمانبندی آنها دارند. این هزینهها در موارد معمولی کمترین تأثیر را دارند، اما زمانی که تعداد زیادی گوروتین بدون مدیریت صحیح ایجاد میشوند، میتوانند به مشکلات عملکردی بزرگی منجر شوند. همچنین، گوروتینهایی که بدون مدیریت زمانهای چرخه حیات مشخصی ایجاد میشوند، میتوانند به مشکلات دیگری نیز منجر شوند، مثل جلوگیری از جمعآوری زبالهها (garbage collected) و نگهداشتن منابعی که دیگر استفاده نمیشوند.
-
-بنابراین، از لو رفتن (leak) گوروتینها در کد تولیدی (production code) جلوگیری کنید. برای تست نشتی گوروتین داخل پکیجهایی که ممکن است گوروتین ایجاد کنند، از [go.uber.org/goleak](https://pkg.go.dev/go.uber.org/goleak) استفاده کنید.
-
-بطور کلی، هر گوروتین باید:
-
-- یک زمان پیشبینیشده برای متوقف شدن داشته باشد؛ یا
-- باید یک راه برای اعلام به گوروتین وجود داشته باشد که باید متوقف شود.
-
-در هر دو مورد، باید یک روش وجود داشته باشد تا کد بلاک شده و منتظر اتمام گوروتین شود.
-
-برای مثال:
-
-
-بد | خوب |
-
-
-
-```go
-go func() {
- for {
- flush()
- time.Sleep(delay)
- }
-}()
-```
-
- |
-
-```go
-var (
- stop = make(chan struct{}) // به گوروتین می گوید که متوقف شود
- done = make(chan struct{}) // به ما می گوید که گوروتین خارج شد
-)
-go func() {
- defer close(done)
-
- ticker := time.NewTicker(delay)
- defer ticker.Stop()
- for {
- select {
- case <-ticker.C:
- flush()
- case <-stop:
- return
- }
- }
-}()
-
-// خارج از محدوده گوروتین(در جایی دیگر)...
-close(stop) // به گوروتین علامت دهید که متوقف شود
-<-done // و صبر کنید تا خارج شود
-```
-
- |
-
-
-هیچ راهی برای متوقف کردن این گوروتین وجود ندارد. گوروتین تا زمانی که برنامه خارج شود اجرا می شود.
-
- |
-
-این گوروتین را می توان با `close(stop)` متوقف کرد، و می توانیم منتظر خروج آن با `done->` باشیم.
-
- |
-
-
-#### منتظر خروج گوروتین ها باشید
-
-با توجه به گوروتین ایجاد شده توسط سیستم، باید راهی برای انتظار خروج گوروتین وجود داشته باشد. دو روش رایج برای انجام این کار وجود دارد:
-
-- اگر چندین گوروتین دارید که میخواهید منتظر آنها بمانید از `sync.WaitGroup` استفاده کنید.
-
- ```go
- var wg sync.WaitGroup
- for i := 0; i < N; i++ {
- wg.Add(1)
- go func() {
- defer wg.Done()
- // ...
- }()
- }
-
- // صبر کنید تا همه چیز تمام شود:
- wg.Wait()
- ```
-
-- اگر تنها یک گوروتین وجود دارد، بهتر است یک کانال `{}chan struct` دیگر ایجاد کنید که گوروتین آن را پس از انجام کار ببندد. به این ترتیب میتوانید انتظار برای اتمام گوروتین را داشته باشید. این کار به شما اجازه میدهد تا بدون استفاده از `sync.WaitGroup` منتظر اتمام گوروتین باشید.
-
- ```go
- done := make(chan struct{})
- go func() {
- defer close(done)
- // ...
- }()
-
- // برای اینکه منتظر بمانید تا کار گوروتین تمام شود:
- <-done
- ```
-
-#### از گوروتین ها در تابع `()init` استفاده نکنید
-
-`init()` functions should not spawn goroutines.
-See also [Avoid init()](#avoid-init).
-
-توابع `()init` نباید گوروتینها را راهاندازی کنند. همچنین دیگر مواردی که استفاده از [تابع ()init را توصیه نمیکند](#avoid-init):
-
-اگر یک بسته (package) نیاز به یک گوروتین پسزمینه دارد، باید یک شی ارائه دهد که مسئولیت مدیریت چرخه حیات گوروتین را بر عهده دارد. این شی باید یک متد (مانند `Close`, `Stop`, `Shutdown` و غیره) ارائه دهد که به گوروتین پسزمینه اعلام کند که باید متوقف شود و منتظر اتمام آن بماند.
-
-
-بد | خوب |
-
-
-
-```go
-func init() {
- go doWork()
-}
-
-func doWork() {
- for {
- // ...
- }
-}
-```
-
- |
-
-```go
-type Worker struct{ /* ... */ }
-
-func NewWorker(...) *Worker {
- w := &Worker{
- stop: make(chan struct{}),
- done: make(chan struct{}),
- // ...
- }
- go w.doWork()
-}
-
-func (w *Worker) doWork() {
- defer close(w.done)
- for {
- // ...
- case <-w.stop:
- return
- }
-}
-
-// خاموش شدن (Shutdown) به workder می گوید که متوقف شود و صبر کند تا کار تمام شود.
-func (w *Worker) Shutdown() {
- close(w.stop)
- <-w.done
-}
-```
-
- |
-
-
-زمانی که کاربر این بسته (package) را (export) میکند، یک گوروتین پسزمینه بدون شرطی ایجاد میشود.
-کاربر هیچ کنترلی بر روی گوروتین ندارد و هیچ وسیلهای برای متوقف کردن آن وجود ندارد.
-
-
- |
-
-گوروتین، worker را فقط در صورتی ایجاد میکند که کاربر آن را درخواست کند. همچنین امکانی برای shutdownکردن worker فراهم میکند تا کاربر بتواند منابع مورد استفاده توسط worker را آزاد کند.
-
-توجه داشته باشید که اگر worker مدیریت چندین گوروتین را انجام میدهد، باید از `WaitGroups` استفاده کنید. برای جزئیات بیشتر به [منتظر اتمام گوروتینها باشید](#منتظر-خروج-گوروتین-ها-باشید)
-
- |
-
-
-## کارایی (Performance)
-
-دستورالعملهای مربوط به عملکرد، تنها به مسیر اصلی (hot path) اعمال میشوند.
-
-### پکیج `strconv` را به `fmt` ترجیح دهید
-
-وقتی میخواهید primitives را به string تبدیل کنید یا برعکس، بهتر است از بسته `strconv` استفاده کنید چرا که عملکرد این بسته سریعتر از بسته `fmt` است.
-
-
-بد | خوب |
-
-
-
-```go
-for i := 0; i < b.N; i++ {
- s := fmt.Sprint(rand.Int())
-}
-```
-
- |
-
-```go
-for i := 0; i < b.N; i++ {
- s := strconv.Itoa(rand.Int())
-}
-```
-
- |
-
-
-```plain
-BenchmarkFmtSprint-4 143 ns/op 2 allocs/op
-```
-
- |
-
-```plain
-BenchmarkStrconv-4 64.2 ns/op 1 allocs/op
-```
-
- |
-
-
-### از تبدیل رشته به بایت (string-to-byte) خودداری کنید
-
-به طور مکرر برشهای بایت (byte slices) را از stringهای ثابت ایجاد نکنید. بجای اینکار، یک تبدیل انجام دهید و نتیجه را ثبت کنید.
-
-
-Bad | Good |
-
-
-
-```go
-for i := 0; i < b.N; i++ {
- w.Write([]byte("Hello world"))
-}
-```
-
- |
-
-```go
-data := []byte("Hello world")
-for i := 0; i < b.N; i++ {
- w.Write(data)
-}
-```
-
- |
-
-
-```plain
-BenchmarkBad-4 50000000 22.2 ns/op
-```
-
- |
-
-```plain
-BenchmarkGood-4 500000000 3.25 ns/op
-```
-
- |
-
-
-### ترجیحا ظرفیت کانتینر (container) را مشخص کنید
-
-تا جایی که امکان دارد ظرفیت کانتینر را مشخص کنید تا حافظه از قبل برای کانتینر تخصیص داده شود. این امر تخصیص های بعدی (با کپی و تغییر اندازه ظرف) را هنگام افزودن عناصر به حداقل می رساند.
-
-#### تعیین حداکثر ظرفیت ممکن Map
-
-در صورت امکان، هنگام مقداردهی اولیه Mapها با `()make` اندازه ظرفیت آن را مشخص کنید.
-
-```go
-make(map[T1]T2, hint)
-```
-
-مشخص کردن ظرفیت به ()make باعث ایجاد Map در زمان مقداردهی اولیه میشود، که در صورت اضافه شدن عناصر به Map، از تخصیص مجدد حافظه برای Map جلوگیری میکند.
-
-در واقعیت، تعیین ظرفیت Map با استفاده از تابع ()make نمیتواند به صورت دقیق و کامل تعداد دقیق buckets مورد نیاز برای یک hashmap را پیشبینی کند. به جای اینکه به صورت کامل پیشبینی شده باشد، این تعیین ظرفیت تقریبا buckets مورد نیاز برای hashmap را ارائه میدهد. به عبارت دیگر، حتی با تعیین یک ظرفیت خاص، ممکن است در هنگام افزودن عناصر به Map، تخصیصها (allocation) انجام شود.
-
-
-بد | خوب |
-
-
-
-```go
-m := make(map[string]os.FileInfo)
-
-files, _ := os.ReadDir("./files")
-for _, f := range files {
- m[f.Name()] = f
-}
-```
-
- |
-
-```go
-
-files, _ := os.ReadDir("./files")
-
-m := make(map[string]os.DirEntry, len(files))
-for _, f := range files {
- m[f.Name()] = f
-}
-```
-
- |
-
-
-متغیر `m` بدون تعیین اندازه ایجاد شده است؛ بنابراین ممکن است در زمان اختصاص (assignment) عناصر به `m` تخصیصهای بیشتری ایجاد شود.
-
- |
-
-متغیر `m` با یک اشاره به اندازه ایجاد شده است؛ بنابراین ممکن است در زمان اختصاص (assignment) عناصر به `m` تخصیصهای کمتری ایجاد شود.
-
- |
-
-
-#### تعیین ظرفیت برش(slice)
-
-در صورت امکان، هنگام مقداردهی اولیه sliceها با استفاده از تابع `()make`، مقدار ظرفیت راتعیین کنید، به ویژه هنگام اضافه کردن عناصر.
-
-```go
-make([]T, length, capacity)
-```
-
-برخلاف Mapها، ظرفیت برش(Slice) نیازی به مشخص کردن ظرفیت آرایه در زمان ایجاد آن ندارد: به این معنا که عملیاتهای بعدی `()append` هیچ تخصیص حافظهای را در پی ندارند (تا زمانی که طول آرایه با ظرفیت مطابقت داشته باشد، پس از آن هر append به منظور نگهداری عناصر اضافی نیاز به تغییر اندازه دارد).
-
-
-بد | خوب |
-
-
-
-```go
-for n := 0; n < b.N; n++ {
- data := make([]int, 0)
- for k := 0; k < size; k++{
- data = append(data, k)
- }
-}
-```
-
- |
-
-```go
-for n := 0; n < b.N; n++ {
- data := make([]int, 0, size)
- for k := 0; k < size; k++{
- data = append(data, k)
- }
-}
-```
-
- |
-
-
-```plain
-BenchmarkBad-4 100000000 2.48s
-```
-
- |
-
-```plain
-BenchmarkGood-4 100000000 0.21s
-```
-
- |
-
-
-## استایل (style)
-
-### از خطوط بیش از حد طولانی خودداری کنید
-
-از خطوط کدی که خوانندگان را ملزم به اسکرول افقی یا چرخاندن بیش از حد سر خود می کند اجتناب کنید.
-
-ما محدودیت طول خط نرم **99 کاراکتر** را توصیه می کنیم. نویسندگان باید قبل از رسیدن به این حد، خطوط را wrap کنند، اما این یک محدودیت دقیق نیست. اجازه داده شده است که کد این محدودیت را تجاوز کند.
-
-### یکپارچگی را رعایت کنید
-
-برخی از معیارهای ذکر شده در این مقاله، ارزیابی های عینی، بر اساس موقعیت یا سناریو، زمینه (context)، یا قضاوت های ذهنی هستند.
-
-مهمتر از همه اینا، **پیوستگی را حفظ کنید**.
-
-کد یکنواخت و یکدست راحتتر ویرایش میشود، منطقیتر است، نیاز به تفکر کمتری دارد، و همچنین راحتتر میتوان آن را بهروز کرد و رفع اشکالها در آن آسانتر است.
-
-به عبارت دیگر، داشتن چندین سبک مختلف کدنویسی در یک پایگاه کد میتواند منجر به هزینههای سربار تعمیر و نگهداری، عدم انسجام و عدم تطابق در استایلها یا نگارش کد میشود در نهایت همه اینها مستقیماً منجر به کاهش سرعت، بررسی کدهای پیچیده و افزایش تعداد اشکال میشود.
-
-هنگام اعمال این استانداردها در یک codebase، توصیه میشود که تغییرات در سطح پکیج (یا بزرگتر) اعمال شود، با اجرای این تغییرات در سطح زیربسته (sub-package) میتواند نگرانیهای فوق را با معرفی چندین سبک در یک کد نقض کند.
-
-### تعاریف مشابه را گروه بندی کنید
-
-زبان Go از گروهبندی اعلانهای مشابه پشتیبانی میکند.
-
-
-بد | خوب |
-
-
-
-```go
-import "a"
-import "b"
-```
-
- |
-
-```go
-import (
- "a"
- "b"
-)
-```
-
- |
-
-
-همین امر در مورد ثابتها، متغیرها و اعلان تایپها صدق میکند:
-
-
-بد | خوب |
-
-
-
-```go
-
-const a = 1
-const b = 2
-
-
-
-var a = 1
-var b = 2
-
-
-
-type Area float64
-type Volume float64
-```
-
- |
-
-```go
-const (
- a = 1
- b = 2
-)
-
-var (
- a = 1
- b = 2
-)
-
-type (
- Area float64
- Volume float64
-)
-```
-
- |
-
-
-فقط اعلانهای مرتبط را گروهبندی کنید. اعلانهای غیرمرتبط را گروهبندی نکنید.
-
-
-بد | خوب |
-
-
-
-```go
-type Operation int
-
-const (
- Add Operation = iota + 1
- Subtract
- Multiply
- EnvVar = "MY_ENV"
-)
-```
-
- |
-
-```go
-type Operation int
-
-const (
- Add Operation = iota + 1
- Subtract
- Multiply
-)
-
-const EnvVar = "MY_ENV"
-```
-
- |
-
-
-هیچ محدودیتی برای استفاده از گروهها وجود ندارد، به عنوان مثال: میتوانید از آنها در داخل توابع استفاده کنید:
-
-
-بد | خوب |
-
-
-
-```go
-func f() string {
- red := color.New(0xff0000)
- green := color.New(0x00ff00)
- blue := color.New(0x0000ff)
-
- // ...
-}
-```
-
- |
-
-```go
-func f() string {
- var (
- red = color.New(0xff0000)
- green = color.New(0x00ff00)
- blue = color.New(0x0000ff)
- )
-
- // ...
-}
-```
-
- |
-
-
-استثنا: اعلانهای متغیر (مخصوصاً آنهایی که درون توابع هستند) در صورت مجاورت با متغیرهای دیگر باید با هم گروهبندی شوند. این کار را برای متغیرهای اعلام شده با هم انجام دهید، حتی اگر نامرتبط باشند.
-
-
-بد | خوب |
-
-
-
-```go
-func (c *client) request() {
- caller := c.name
- format := "json"
- timeout := 5*time.Second
- var err error
-
- // ...
-}
-```
-
- |
-
-```go
-func (c *client) request() {
- var (
- caller = c.name
- format = "json"
- timeout = 5*time.Second
- err error
- )
-
- // ...
-}
-```
-
- |
-
-
-### مرتب سازی گروهی واردات (imports)
-
-واردات باید به دو دسته تقسیم شود:
-
-- کتابخانه استاندارد
-- سایر کتابخانه ها
-این گروه بندی است که توسط goimports به طور پیش فرض اعمال می شود.
-
-
-بد | خوب |
-
-
-
-```go
-import (
- "fmt"
- "os"
- "go.uber.org/atomic"
- "golang.org/x/sync/errgroup"
-)
-```
-
- |
-
-```go
-import (
- "fmt"
- "os"
-
- "go.uber.org/atomic"
- "golang.org/x/sync/errgroup"
-)
-```
-
- |
-
-
-### نام گذاری بسته ها (Package Names)
-
-هنگام نامگذاری بستهها(packages)، نامی را انتخاب کنید که:
-
-- تمام حروف کوچک. بدون حروف بزرگ یا زیرخط.
-- در بیشتر موارد هنگام استفاده از واردات نامگذاری شده، تغییر نام مورد نیاز نیست.
-- کوتاه و مختصر باشد. به یاد داشته باشید که نام را در هر کجا که استفاده می شود کاملاً مشخص کنید.
-نیازی به جمع نیست. به عنوان مثال net/url، نه net/urls.
-- از "common"، "util"، "shared" یا "lib" استفاده نکنید. اینها اسامی بد و بی معنی هستند.
-
-همچنین [راهنمای نامگذاری بسته(Package Names)](https://blog.golang.org/package-names) و [راهنمای استایل بسته(package) Go](https://rakyll.org/style-packages/) را ببینید.
-
-### نام گذاری توابع (Function Names)
-
-ما از روش رایج جامعه Go با استفاده از [MixedCaps برای نامگذاری توابع](https://golang.org/doc/effective_go.html#mixed-caps) پیروی میکنیم. یک استثناء برای توابع تست وجود دارد که ممکن است شامل زیرخط (_) به منظور گروهبندی موارد تست مرتبط باشد، به عنوان مثال،
-`TestMyFunction_WhatIsBeingTested`.
-
-### نام مستعار واردات (Import)
-
-در صورتی که نام پکیج با آخرین بخش مسیر (import path) مطابقت نداشته باشد، باید از نامگذاری مخفف (aliasing) برای import استفاده شود.
-
-```go
-import (
- "net/http"
-
- client "example.com/client-go"
- trace "example.com/trace/v2"
-)
-```
-
-در سایر موارد، باید از نامگذاری مخفف (aliasing) در import خودداری شود مگر اینکه تداخل مستقیمی بین imports وجود داشته باشد.
-
-
-بد | خوب |
-
-
-
-```go
-import (
- "fmt"
- "os"
-
-
- nettrace "golang.net/x/trace"
-)
-```
-
- |
-
-```go
-import (
- "fmt"
- "os"
- "runtime/trace"
-
- nettrace "golang.net/x/trace"
-)
-```
-
- |
-
-
-### گروه بندی و مرتب سازی توابع
-
-- توابع باید بر اساس تقریبی فراخوانیشان مرتب شوند(منظور این است که توابعی که بیشترین احتمال برای فراخوانی آنها وجود دارد در ابتدا آورده میشوند و توابعی که کمترین احتمال فراخوانی رو دارند در انتها قرار میگیرند).
-- توابع موجود در یک فایل باید بر اساس گیرنده (receiver) گروه بندی شوند(منظور این است که توابعی که بر روی یک نوع خاص عمل میکنند، در یک قسمت مشخص از کد قرار میگیرند. این کار به ترتیب و منظم شدن کدها کمک میکند و به توسعه دهندگان کمک میکند تا توابع مرتبط با هم را به راحتی پیدا کنند).
-
-بنابراین، توابع صادرشده (exported) باید بعد از تعریفهای `struct`, `const`, `var` در ابتدای فایل ظاهر شوند.
-
-توابعی که با `()newXYZ`/`()NewXYZ` شروع میشوند، ممکن است بعد از تعریف نوع (type) قبل از باقی متدهای دریافتکننده (receiver) ظاهر شوند.
-
-از آنجایی که توابع توسط گیرنده (receiver) گروهبندی میشوند، توابع utility باید در انتهای فایل ظاهر شوند.
-
-
-بد | خوب |
-
-
-
-```go
-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{}
-}
-```
-
- |
-
-```go
-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 {...}
-```
-
- |
-
-
-### تورفتگی (Nesting) را کاهش دهید
-
-کد باید سعی کند تورفتگی (nesting) را به حداقل برساند. برای این کار، ابتدا موارد خطا یا شرایط خاص را بررسی و پردازش کند و در صورت لزوم به سرعت از تابع خارج شود یا به مرحله بعد بروند. همچنین باید تلاش کند تا تعداد کدهایی که به چندین سطح تورفتگی وارد میشوند را کاهش دهد.
-
-
-بد | خوب |
-
-
-
-```go
-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)
- }
-}
-```
-
- |
-
-```go
-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()
-}
-```
-
- |
-
-
-### اجتناب از Elseهای غیر ضروری
-
-اگر یک متغیر در هر دو شاخه (شرط true و شرط false) یک دستور if تنظیم میشود، میتوانید از یک if تنها استفاده کنید.
-
-
-بد | خوب |
-
-
-
-```go
-var a int
-if b {
- a = 100
-} else {
- a = 10
-}
-```
-
- |
-
-```go
-a := 10
-if b {
- a = 100
-}
-```
-
- |
-
-
-### تعاریف متغیرهای سطح بالا
-
-در ابتدای کد، از واژه کلیدی معمول `var` استفاده کنید. در صورتی که نوع متغیر مطابق نوع عبارت مقداردهی باشد، نیازی به مشخص کردن نوع نیست.
-
-
-بد | خوب |
-
-
-
-```go
-var _s string = F()
-
-func F() string { return "A" }
-```
-
- |
-
-```go
-var _s = F()
-// از آنجایی که F قبلاً بیان می کند که یک رشته را برمی گرداند، نیازی به تعیین مجدد نوع آن نداریم.
-
-func F() string { return "A" }
-```
-
- |
-
-
-اگر نوع عبارت دقیقاً با نوع مورد نیاز مطابقت ندارد، نوع آن را مشخص کنید.
-
-```go
-type myError struct{}
-
-func (myError) Error() string { return "error" }
-
-func F() myError { return myError{} }
-
-var _e error = F()
-// F یک شی از نوع myError را برمی گرداند اما ما خطا می خواهیم.
-```
-
-### از پیشوند `"_"` برای متغیرهای خصوصی (Unexported) استفاده کنید
-
-به منظور افزایش دقت و وضوح، متغیرها(`var`s) و ثابتهایی(`const`s) که عموماً در سطح بالای کد (یعنی در دسترسی پکیج) قرار میگیرند، با استفاده از نشانه "_" (زیرخط) قبل از نام آنها ترکیب شوند. این کار باعث میشود که هنگام استفاده از آنها در دیگر بخشهای کد، به وضوح متوجه شود که این متغیرها و ثابتها به عنوان نمادهای سراسری (global) در نظر گرفته شوند.
-
-دلیل: متغیرها و ثابتهای سطح بالا در محدودهی پکیج قرار دارند و تا حدی کلی هستند. استفاده از نامهای عمومی ممکن است باعث اشتباه در استفاده از مقادیر اشتباه در فایلهای دیگر شود.
-
-
-بد | خوب |
-
-
-
-```go
-// foo.go
-
-const (
- defaultPort = 8080
- defaultUser = "user"
-)
-
-// bar.go
-
-func Bar() {
- defaultPort := 9090
- ...
- fmt.Println("Default port", defaultPort)
-
- // اگر خط اول Bar() حذف شود، خطای کامپایل نخواهیم دید.
-}
-```
-
- |
-
-```go
-// foo.go
-
-const (
- _defaultPort = 8080
- _defaultUser = "user"
-)
-```
-
- |
-
-
-**استثنا**: در مواردی که مقادیر خطا (error) به صورت unexported باشند، میتوانید از پیشوند `err` بدون خط زیر (underscore) استفاده کنید. به منظور اطلاعات بیشتر در مورد نامگذاری خطا، به [نامگذاری خطا](#نام-گذاری-خطا) مراجعه کنید.
-
-### جاسازی (Embedding) در ساختارها
-
-اگر نوعهای تو در تو (embedded types) در یک struct وجود دارند، آنها باید در بالای لیست فیلدهای struct قرار گیرند، و باید یک خط خالی بین فیلدهای تو در تو و فیلدهای معمولی وجود داشته باشد.
-
-
-بد | خوب |
-
-
-
-```go
-type Client struct {
- version int
- http.Client
-}
-```
-
- |
-
-```go
-type Client struct {
- http.Client
-
- version int
-}
-```
-
- |
-
-
-درج نوعهای دیگر در یک ساختار (embedding) باید به صورتی باشد که به ویژگیها یا قابلیتها به یک شکل منطقی و معنادار افزوده یا تقویت کند. این کار باید بدون تأثیر منفی قابل مشاهده برای کاربران انجام شود (برای اطلاعات بیشتر، "همچنین: [از جاسازی نوعها (Embedding Types) در ساختارهای عمومی خودداری کنید](#از-جاسازی-نوع-ها-embedding-types-در-ساختارهای-عمومی-خودداری-کنید)" را ببینید).
-
-استثناء: حتی در نوعهای (unexported) هم، Mutexها (قفلهای همزمانی) نباید به صورت تعبیه شده درج شوند. همچنین میتوانید به [مقدار صفر (zero-value) Mutexها معتبر هستند](#مقدار-صفر-zero-value-mutexها-معتبر-هستند) مراجعه کنید.
-
-تعبیه (Embedding) **نباید:**
-
-- صرفا به منظور زیبایی یا افزایش راحتی باشد.
-- ساختن یا استفاده از نوعهای خارجی را پیچیدهتر کند.
-- باعث تغییر در مقدار-صفر (zero value) نوع خارجی شود. . اگر نوع خارجی، مقدار صفر مفیدی دارد، پس از تعبیه نوع داخلی، همچنان باید مقدار صفر مفید داشته باشد.
-- توابع یا فیلدهای غیرمرتبط از نوع خارجی را به عنوان نتیجه تعبیه نمایش دهد.
-- نوعهای (unexported) را نمایش دهد.
-- اثرات کپی (copy) انواع خارجی را تغییر دهد.
-- API یا معناشناسی انواع خارجی را تغییر دهد.
-- یک نمایش غیرمعمول از نوع داخلی را ارائه دهد.
-- جزئیات پیادهسازی نوع خارجی را نشان دهد.
-- به کاربران اجازه مشاهده یا کنترل اطلاعات داخلی نوع را بدهد.
-- با تغییر رفتار کلی عملکردهای داخلی موقعیتهای غیرمنتظره ای را برای کاربران به ارمغان بیاورد.
-
-بطور کلی، تعبیه (Embedding) باید با آگاهی و هدف انجام شود. یک آزمون ساده برای این کار این است: "آیا تمام این متدها/فیلدها باید به صورت مستقیم به نوع خارجی اضافه شوند؟" اگر پاسخ "بله" باشد، معقول است که تعبیه انجام شود؛ اگر پاسخ "بخشی از آنها" یا "خیر" باشد، بهتر است از یک فیلد به جای تعبیه استفاده کنید.
-
-
-بد | خوب |
-
-
-
-```go
-type A struct {
-// بد: حالا دستورهای A.Lock() و A.Unlock() در دسترس هستند،
-// اما فایدهای ندارند و به کاربران اجازه میدهند که
-// جزئیات داخلی A را کنترل کنند.
- sync.Mutex
-}
-```
-
- |
-
-```go
-type countingWriteCloser struct {
-// خوب: تابع Write() در این لایه بیرونی برای
-// یک هدف خاص فراهم شده است و کار را به
-// تابع Write() نوع داخلی انتقال میدهد.
- io.WriteCloser
-
- count int
-}
-
-func (w *countingWriteCloser) Write(bs []byte) (int, error) {
- w.count += len(bs)
- return w.WriteCloser.Write(bs)
-}
-```
-
- |
-
-
-```go
-type Book struct {
- // بد: اشارهگر سودمندی مقدار-صفر را تغییر میدهد
- io.ReadWriter
-
- // other fields
-}
-
-// later
-
-var b Book
-b.Read(...) // panic: nil pointer
-b.String() // panic: nil pointer
-b.Write(...) // panic: nil pointer
-```
-
- |
-
-```go
-type Book struct {
- // خوب: دارای مقدار-صفر مفید است
- bytes.Buffer
-
- // other fields
-}
-
-// later
-
-var b Book
-b.Read(...) // ok
-b.String() // ok
-b.Write(...) // ok
-```
-
- |
-
-
-```go
-type Client struct {
- sync.Mutex
- sync.WaitGroup
- bytes.Buffer
- url.URL
-}
-```
-
- |
-
-```go
-type Client struct {
- mtx sync.Mutex
- wg sync.WaitGroup
- buf bytes.Buffer
- url url.URL
-}
-```
-
- |
-
-
-### تعاریف متغیرهای محلی
-
-اگر یک متغیر به صراحت به یک مقدار تنظیم میشود، باید از اعلانهای کوتاه متغیر (`:=`) استفاده شود.
-
-
-بد | خوب |
-
-
-
-```go
-var s = "foo"
-```
-
- |
-
-```go
-s := "foo"
-```
-
- |
-
-
-با این حال، مواردی وجود دارد که در آن مقدار پیشفرض وقتی که از واژه کلیدی var استفاده میشود، واضحتر است. برای مثال، در [اعلان برشهای خالی](https://github.com/golang/go/wiki/CodeReviewComments#declaring-empty-slices) (Empty Slices).
-
-
-بد | خوب |
-
-
-
-```go
-func f(list []int) {
- filtered := []int{}
- for _, v := range list {
- if v > 10 {
- filtered = append(filtered, v)
- }
- }
-}
-```
-
- |
-
-```go
-func f(list []int) {
- var filtered []int
- for _, v := range list {
- if v > 10 {
- filtered = append(filtered, v)
- }
- }
-}
-```
-
- |
-
-
-### خود `nil` یک برش `slice` معتبر است
-
- خود `nil` به عنوان یک برش با طول صفر (length 0) معتبر شناخته میشود. این بدان معناست که:
-
-- شما نباید به صورت صریح یک برش با طول صفر را برگردانید. به جای آن باید `nil` را برگردانید.
-
-
- بد | خوب |
-
-
-
- ```go
- if x == "" {
- return []int{}
- }
- ```
-
- |
-
- ```go
- if x == "" {
- return nil
- }
- ```
-
- |
-
-
-- برای بررسی اینکه آیا یک برش (slice) خالی است یا نه، همیشه از عبارت `len(s) == 0` استفاده کنید.نباید برای بررسی خالی بودن از `nil` استفاده کنید.
-
-
- بد | خوب |
-
-
-
- ```go
- func isEmpty(s []string) bool {
- return s == nil
- }
- ```
-
- |
-
- ```go
- func isEmpty(s []string) bool {
- return len(s) == 0
- }
- ```
-
- |
-
-
-- مقدار صفر (یک برش که با `var` اعلان شده است) بدون نیاز به استفاده از تابع `make()`، بلافاصله قابل استفاده است.
-
-
- بد | خوب |
-
-
-
- ```go
- nums := []int{}
- // or, nums := make([]int)
-
- if add1 {
- nums = append(nums, 1)
- }
-
- if add2 {
- nums = append(nums, 2)
- }
- ```
-
- |
-
- ```go
- var nums []int
-
- if add1 {
- nums = append(nums, 1)
- }
-
- if add2 {
- nums = append(nums, 2)
- }
- ```
-
- |
-
-
-به خاطر داشته باشید که در حالی که یک برش nil معتبر است، اما با یک برش تخصیص داده شده با طول صفر معادل نیست - یکی از آنها nil است و دیگری نیست - و در شرایط مختلف (مانند فرآیند سریالسازی) ممکن است به صورت متفاوتی مدیریت شوند.
-
-### کاهش دامنه (scope) متغیرها
-
-در صورت امکان، سعی کنید دامنه متغیرها را محدود کنید. مگر اینکه با قانون [تورفتگی (Nesting) را کاهش دهید](#تورفتگی-nesting-را-کاهش-دهید) در تضاد باشد.
-
-
-بد | خوب |
-
-
-
-```go
-err := os.WriteFile(name, data, 0644)
-if err != nil {
- return err
-}
-```
-
- |
-
-```go
-if err := os.WriteFile(name, data, 0644); err != nil {
- return err
-}
-```
-
- |
-
-
-اگر نتیجه یک تابع را بیرون از شرط if نیاز دارید، در اینصورت نباید سعی کنید دامنه متغیر را کاهش دهید.
-
-
-بد | خوب |
-
-
-
-```go
-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
-}
-```
-
- |
-
-```go
-data, err := os.ReadFile(name)
-if err != nil {
- return err
-}
-
-if err := cfg.Decode(data); err != nil {
- return err
-}
-
-fmt.Println(cfg)
-return nil
-```
-
- |
-
-
-### از پارامترهای بی نام (Naked Parameters) خودداری کنید
-
-پارامترهای بینام در فراخوانی توابع میتوانند خوانایی را کاهش دهند. در صورتی که معنای پارامترها واضح نباشد، نامهای پارامترها را با کامنت استایل C (`/* ... */`) اضافه کنید.
-
-
-بد | خوب |
-
-
-
-```go
-// func printInfo(name string, isLocal, done bool)
-
-printInfo("foo", true, true)
-```
-
- |
-
-```go
-// func printInfo(name string, isLocal, done bool)
-
-printInfo("foo", true /* isLocal */, true /* done */)
-```
-
- |
-
-
-بهتر است پارامترهای `bool` بدون نوع خاص را با نوعهای سفارشی جایگزین کنید تا کد خواناتر و ایمنتری داشته باشید. این امکان را به شما میدهد تا در آینده بیش از دو وضعیت (true/false) برای این پارامتر داشته باشید.
-
-```go
-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)
-```
-
-### استفاده از `Raw String Literals` برای جلوگیری از Escape شدن کاراکترها
-
-زبان Go از رشتههای متنی خام ([raw string literals](https://golang.org/ref/spec#raw_string_lit)) پشتیبانی میکند. این نوع رشتهها میتوانند از چندین خط تشکیل شده و شامل نقل قولها باشند. برای افزایش خوانایی کد و جلوگیری از استفاده از رشتههای دستساز با ویژگیهای خاص، از رشتههای متنی خام استفاده کنید. این نوع رشتهها خوانایی کد را خیلی بالا میبرند.
-
-
-بد | خوب |
-
-
-
-```go
-wantError := "unknown name:\"test\""
-```
-
- |
-
-```go
-wantError := `unknown error:"test"`
-```
-
- |
-
-
-### مقداردهی اولیه ساختارها (structs)
-
-#### استفاده از نام فیلدها برای مقداردهی اولیه ساختارها
-
-تقریباً همیشه باید نام فیلدها را هنگام مقداردهی اولیه ساختارها (structs) مشخص کنید. این توصیه اکنون توسط ابزار [`go vet`](https://golang.org/cmd/vet/) اجباری شده است.
-
-
-بد | خوب |
-
-
-
-```go
-k := User{"John", "Doe", true}
-```
-
- |
-
-```go
-k := User{
- FirstName: "John",
- LastName: "Doe",
- Admin: true,
-}
-```
-
- |
-
-
-استثنا: زمانی که تعداد فیلدها سه یا کمتر باشد میتوانید نام فیلدها را در جداول تست حذف کنید.
-
-```go
-tests := []struct{
- op Operation
- want string
-}{
- {Add, "add"},
- {Subtract, "subtract"},
-}
-```
-
-#### حذف فیلدهای مقدارصفر (zero value) در ساختارها
-
-در هنگام مقداردهی اولیه به ساختارها (structs) با استفاده از نام فیلدها، فیلدهایی که مقدار صفر (zero value) دارند را حذف کنید مگر اینکه به دلایل معناداری نیاز به آنها داشته باشید. در غیر این صورت، به Go اجازه دهید این فیلدها را به طور خودکار به مقادیر صفر تنظیم کند.
-
-
-بد | خوب |
-
-
-
-```go
-user := User{
- FirstName: "John",
- LastName: "Doe",
- MiddleName: "",
- Admin: false,
-}
-```
-
- |
-
-```go
-user := User{
- FirstName: "John",
- LastName: "Doe",
-}
-```
-
- |
-
-
-این به کاهش نویز برای خوانندگان با حذف مقادیر پیشفرض در آن زمینه کمک میکند. فقط مقادیر معنی دار مشخص شده است.
-
-در صورتی که نام فیلدها مفهومی داشته باشند، مقادیر صفر (zero values) را نیز در نظر بگیرید. به عنوان مثال، در [جداول تست (Table-driven tests)](#جداول-تست-table-driven-tests)، استفاده از نام فیلدها حتی زمانی که این مقادیر صفری (zero values) هستند میتواند مفید باشد.
-
-```go
-tests := []struct{
- give string
- want int
-}{
- {give: "0", want: 0},
- // ...
-}
-```
-
-#### استفاده از `var` برای ساختارهای مقدارصفر (zero value)
-
-زمانی که تمامی فیلدهای یک ساختار (struct) در یک اعلان حذف شدند، از شکل `var` برای اعلان ساختار استفاده کنید.
-
-
-بد | خوب |
-
-
-
-```go
-user := User{}
-```
-
- |
-
-```go
-var user User
-```
-
- |
-
-
-این کار باعث تفکیک ساختارهای با مقدار صفر از ساختارهای دارای فیلدهای غیر صفر میشود، مشابه تفکیکی که برای [مقداردهی اولیه Mapها](#مقداردهی-اولیه-mapها) ایجاد شده است، و با روش ترجیحی ما برای [اعلان برشهای خالی](https://github.com/golang/go/wiki/CodeReviewComments#declaring-empty-slices) هماهنگ میشود.
-
-#### مقداردهی اولیه ساختارهای رفرنس دار
-
-از `&T{}` به جای `new(T)` هنگام مقداردهی اولیه ساختار (struct references) استفاده کنید تا با مقداردهی اولیه ساختار مطابقت داشته باشد.
-
-
-بد | خوب |
-
-
-
-```go
-sval := T{Name: "foo"}
-
-// ناسازگار
-sptr := new(T)
-sptr.Name = "bar"
-```
-
- |
-
-```go
-sval := T{Name: "foo"}
-
-sptr := &T{Name: "bar"}
-```
-
- |
-
-
-### مقداردهی اولیه Mapها
-
-برای ایجاد نقشههای خالی و نقشههایی که به صورت برنامهنویسی پر میشوند، استفاده از تابع `(..)make` توصیه میشود. این اقدام نه تنها مقداردهی نقشه را از اعلان آن به صورت بصری متمایز میکند، بلکه اگر در آینده اندازه (size hints) در دسترس قرار بگیرد، امکان اضافه کردن آنها را آسان میسازد.
-
-
-بد | خوب |
-
-
-
-```go
-var (
- // m1 برای خواندن و نوشتن امن است.
- // m2 در نوشتن panic خواهد کرد.
- m1 = map[T1]T2{}
- m2 map[T1]T2
-)
-```
-
- |
-
-```go
-var (
- // m1 برای خواندن و نوشتن امن است.
- // m2 در نوشتن panic خواهد کرد.
- m1 = make(map[T1]T2)
- m2 map[T1]T2
-)
-```
-
- |
-
-
-اعلان و مقداردهی اولیه از نظر بصری مشابه هستند.
-
- |
-
-اعلان و مقداردهی اولیه از نظر بصری متمایز هستند
-
- |
-
-
-در صورت امکان، هنگام مقداردهی اولیه نقشه ها با `make()` اندازه ظرفیت ارائه دهید. برای اطلاعات بیشتر به [تعیین حداکثر ظرفیت ممکن Map](#تعیین-حداکثر-ظرفیت-ممکن-map) مراجعه کنید.
-
-از سوی دیگر، اگر نقشه مجموعهی ثابتی از عناصر را نگه میدارد، از نقشههای لیترال (map literals) برای مقداردهی اولیه استفاده کنید.
-
-
-بد | خوب |
-
-
-
-```go
-m := make(map[T1]T2, 3)
-m[k1] = v1
-m[k2] = v2
-m[k3] = v3
-```
-
- |
-
-```go
-m := map[T1]T2{
- k1: v1,
- k2: v2,
- k3: v3,
-}
-```
-
- |
-
-
-قاعده اساسی در استفاده از نقشهها به شکل زیر است: اگر قرار باشد مجموعهی ثابتی از عناصر را در زمان مقداردهی اولیه اضافه کنید، از نقشههای لیترال (map literals) استفاده کنید. در غیر اینصورت، از تابع `make()` استفاده کنید (و در صورت امکان مقدار ظرفیت را مشخص کنید).
-
-### قالب بندی رشته ها (strings) خارج از تابع `Printf`
-
-اگر شما رشتههای قالببندی (format strings) برای توابع استایلدهی، مانند `Printf` را خارج از رشته معمولی اعلان میکنید، آنها را به عنوان مقادیر `const` ایجاد کنید.
-
-این کمک میکند تا `go vet` تجزیه و تحلیل استاتیک رشته قالببندی را انجام دهد.
-
-
-بد | خوب |
-
-
-
-```go
-msg := "unexpected values %v, %v\n"
-fmt.Printf(msg, 1, 2)
-```
-
- |
-
-```go
-const msg = "unexpected values %v, %v\n"
-fmt.Printf(msg, 1, 2)
-```
-
- |
-
-
-### نام گذاری توابع به سبک `Printf`
-
-وقتی یک تابع با استایل `Printf` اعلان میکنید، مطمئن شوید که `go vet` قادر به شناسایی آن و بررسی رشته قالببندی است.
-
-این بدان معناست که در صورت امکان باید از نامهای پیشتعریف شده برای توابع به سبک Printf استفاده کنید. `go vet` به طور پیشفرض اینها را بررسی میکند. برای اطلاعات بیشتر، به [خانواده Printf](https://golang.org/cmd/vet/#hdr-Printf_family) مراجعه کنید.
-
-اگر نمی توانید از یک نام از پیش تعریف شده استفاده کنید، نامی را که انتخاب می کنید با f خاتمه دهید: مثلاً `Wrapf`، بجای `Wrap`. میتوان از `go vet` بخواهیم نامهای خاص به سبک `Printf` را بررسی کند، اما باید با f خاتمه یابد.
-
-```shell
-go vet -printfuncs=wrapf,statusf
-```
-
-همچنین، میتوانید به مقاله [بررسی خانواده Printf توسط go vet](https://kuzminva.wordpress.com/2017/11/07/go-vet-printf-family-check/) مراجعه کنید.
-
-## الگوها
-
-### جداول تست (Table-driven tests)
-
-استفاده از الگوی تستهای جدولی با [subtests](https://blog.golang.org/subtests) میتواند یک الگوی مفید برای نوشتن تستها باشد تا از تکرار کد در زمانی که منطق آزمون اصلی تکرار میشود جلوگیری شود.
-
-اگر یک سیستم تحت آزمون تست نیاز به آزمایش در برابر _شرایط چندگانه_ دارد که در آن بخشهای خاصی از ورودیها و خروجیها تغییر میکنند، بهترین روش استفاده از تستهای جدولی است. این روش کد را کمتر تکراری میکند و خوانایی آن را بهبود میبخشد.
-
-
-بد | خوب |
-
-
-
-```go
-// func TestSplitHostPort(t *testing.T)
-
-host, port, err := net.SplitHostPort("192.0.2.0:8000")
-require.NoError(t, err)
-assert.Equal(t, "192.0.2.0", host)
-assert.Equal(t, "8000", port)
-
-host, port, err = net.SplitHostPort("192.0.2.0:http")
-require.NoError(t, err)
-assert.Equal(t, "192.0.2.0", host)
-assert.Equal(t, "http", port)
-
-host, port, err = net.SplitHostPort(":8000")
-require.NoError(t, err)
-assert.Equal(t, "", host)
-assert.Equal(t, "8000", port)
-
-host, port, err = net.SplitHostPort("1:8")
-require.NoError(t, err)
-assert.Equal(t, "1", host)
-assert.Equal(t, "8", port)
-```
-
- |
-
-```go
-// func TestSplitHostPort(t *testing.T)
-
-tests := []struct{
- give string
- wantHost string
- wantPort string
-}{
- {
- give: "192.0.2.0:8000",
- wantHost: "192.0.2.0",
- wantPort: "8000",
- },
- {
- give: "192.0.2.0:http",
- wantHost: "192.0.2.0",
- wantPort: "http",
- },
- {
- give: ":8000",
- wantHost: "",
- wantPort: "8000",
- },
- {
- give: "1:8",
- wantHost: "1",
- wantPort: "8",
- },
-}
-
-for _, tt := range tests {
- t.Run(tt.give, func(t *testing.T) {
- host, port, err := net.SplitHostPort(tt.give)
- require.NoError(t, err)
- assert.Equal(t, tt.wantHost, host)
- assert.Equal(t, tt.wantPort, port)
- })
-}
-```
-
- |
-
-
-استفاده از جداول تست (Test tables) کمک میکند تا پیامهای خطا دارای زمینه (context) بیشتری باشند، منطق تکراری را کاهش دهد و امکان افزودن تستهای جدید را فراهم کند.
-
-ما از این قرارداد پیروی میکنیم که برشی از ساختارها (slice of struct) به عنوان تست `tests` مدنظر است و هر مورد آزمون `tt` نامیده میشود. علاوه بر این، ما توصیه میکنیم تا مقادیر ورودی و خروجی برای هر مورد تست را با پیشوندهای `give` و `want` به صورت صریح مشخص کنید.
-
-```go
-tests := []struct{
- give string
- wantHost string
- wantPort string
-}{
- // ...
-}
-
-for _, tt := range tests {
- // ...
-}
-```
-
-#### جلوگیری از پیچیدگیهای غیرضروری در تستهای جدولی
-
-اگر منطق پیچیده یا شرطی در زیرتستها وجود داشته باشد (به عبارت دیگر، منطق پیچیده در داخل حلقه `for`)، تستهای جدولی ممکن است خواندن و نگهداری دشواری داشته باشند و بهتر است از آنها **استفاده نشود**.
-
-تستهای جدولی بزرگ و پیچیده به خوانایی و نگهداری آسیب میزنند زیرا افرادی که تستها را میخوانند ممکن است در اشکالزدایی خطاهایی که در تستها رخ میدهد به مشکل بخورند.
-
-تستهای جدولی مانند این باید به یکی از دو گزینه زیر تقسیم شوند: یا به چندین جدول تست مجزا یا به چندین تابع تست مجزا با نامهای `Test...`
-
-بعضی از اهدافی که باید به آنها دست یابیم عبارتند از:
-
-- تمرکز بر روی بخشهای خاص و محدودی از عملکرد
-- کاهش "عمق تست" و اجتناب از ادعاهای شرطی (راهنمایی زیر را ببینید)
-- اطمینان از استفاده از همهی فیلدهای جدول در تمام تستها
-- اطمینان از اجرای منطق تست برای تمام موارد جدول
-
-در این متن، "عمق تست" به معنای "تعداد ادعاهای متوالی در یک تست داده شده است که نیاز به اثبات ادعاهای قبلی دارند" (مشابه به پیچیدگی سیکلوماتیک) است. داشتن "تستهای کمعمق" به این معناست که تعداد ارتباطات بین ادعاها کمتر است و بهطور مهمتر، این ادعاها به طور پیشفرض کمتر از ادعاهای شرطی هستند.
-
-به طور مشخص، تستهای جدول اگر از مسیرهای انشعاب چندگانه (مانند `shouldError`، `expectCall` و غیره) استفاده کنند، از بسیاری از دستورات `if` برای انتظارات ساختگی خاص (مانند `shouldCallFoo`) استفاده کنند یا توابعی را در داخل جدول قرار دهند (مثلاً `setupMocks func (* FooMock)`) میتوانند گیجکننده باشند و درک آن دشوار شود.
-
-با این حال، هنگام آزمایش رفتاری که فقط بر اساس ورودی تغییر یافته تغییر میکند، موارد مشابه را در یک آزمون جدول با هم گروهبندی میکنیم تا نحوه تغییر رفتار در همه ورودیها را بهتر نشان دهیم، تا اینکه واحدهای قابل مقایسه را به آزمونهای جداگانه تقسیم کنیم و انجام آنها را سختتر کنیم.
-
-اگر بدنه تست کوتاه و ساده باشد، میتوانید برای موارد موفقیت و شکست، یک مسیر اجرایی (شاخه) واحد را در نظر بگیرید که از طریق یک فیلد در جدول تست، مثلاً `shouldErr` برای تعیین انتظارات خطا، انتخاب شود.
-
-
-بد | خوب |
-
-
-
-```go
-func TestComplicatedTable(t *testing.T) {
- tests := []struct {
- give string
- want string
- wantErr error
- shouldCallX bool
- shouldCallY bool
- giveXResponse string
- giveXErr error
- giveYResponse string
- giveYErr error
- }{
- // ...
- }
-
- for _, tt := range tests {
- t.Run(tt.give, func(t *testing.T) {
- // setup mocks
- ctrl := gomock.NewController(t)
- xMock := xmock.NewMockX(ctrl)
- if tt.shouldCallX {
- xMock.EXPECT().Call().Return(
- tt.giveXResponse, tt.giveXErr,
- )
- }
- yMock := ymock.NewMockY(ctrl)
- if tt.shouldCallY {
- yMock.EXPECT().Call().Return(
- tt.giveYResponse, tt.giveYErr,
- )
- }
-
- got, err := DoComplexThing(tt.give, xMock, yMock)
-
- // verify results
- if tt.wantErr != nil {
- require.EqualError(t, err, tt.wantErr)
- return
- }
- require.NoError(t, err)
- assert.Equal(t, want, got)
- })
- }
-}
-```
-
- |
-
-```go
-func TestShouldCallX(t *testing.T) {
- // setup mocks
- ctrl := gomock.NewController(t)
- xMock := xmock.NewMockX(ctrl)
- xMock.EXPECT().Call().Return("XResponse", nil)
-
- yMock := ymock.NewMockY(ctrl)
-
- got, err := DoComplexThing("inputX", xMock, yMock)
-
- require.NoError(t, err)
- assert.Equal(t, "want", got)
-}
-
-func TestShouldCallYAndFail(t *testing.T) {
- // setup mocks
- ctrl := gomock.NewController(t)
- xMock := xmock.NewMockX(ctrl)
-
- yMock := ymock.NewMockY(ctrl)
- yMock.EXPECT().Call().Return("YResponse", nil)
-
- _, err := DoComplexThing("inputY", xMock, yMock)
- assert.EqualError(t, err, "Y failed")
-}
-```
-
- |
-
-
-این پیچیدگی باعث مشکلات در تغییر، درک و اثبات صحت تست میشود.
-
-اگرچه دستورالعملهای دقیقی وجود ندارد، وقتی بین استفاده از تستهای جدولی و تستهای مجزا برای ورودیها/خروجیهای متعدد به یک سیستم تصمیمگیری میکنید، همیشه باید به خوانایی و قابلیت نگهداری فکر کرد.
-
-#### تست های موازی
-
-تستهای موازی، مانند برخی از حلقههای تخصصی (برای مثال، آنهایی که گوروتینها را ایجاد میکنند یا ارجاعها را به عنوان بخشی از بدنه حلقه میگیرند)، باید دقت کنند که متغیرهای حلقه را به صراحت در محدوده حلقه تخصیص دهند تا اطمینان حاصل شود که مقادیر مورد انتظار را نگه میدارند.
-
-```go
-tests := []struct{
- give string
- // ...
-}{
- // ...
-}
-
-for _, tt := range tests {
- tt := tt // for t.Parallel
- t.Run(tt.give, func(t *testing.T) {
- t.Parallel()
- // ...
- })
-}
-```
-
-در مثال بالا، به دلیل استفاده از `t.Parallel()` در زیر حلقه، ما باید یک متغیر `tt` را در دامنه هر تکرار حلقه تعریف کنیم. اگر این کار را انجام ندهیم، بیشتر یا تمام تستها مقدار غیرمنتظرهای برای متغیر `tt` دریافت خواهند کرد یا مقداری که در حال اجرای آنها تغییر میکند.
-
-
-
-### الگوی Functional Options
-
-گزینههای عملکردی (Functional options) الگویی است که در آن یک نوع گزینه (`Option`) غیرشفاف را اعلام میکنید که اطلاعات را در یک ساختار داخلی ثبت میکند. شما تعدادی متغیر از این گزینهها را می پذیرید و بر اساس اطلاعات کاملی که توسط گزینهها در ساختار داخلی ثبت شده است، عمل میکنید.
-
-از این الگو برای آرگومانهای اختیاری در متد سازندهها و سایر واسطهای عمومی (API) که پیشبینی میکنید نیاز به توسعه آنها دارید، استفاده کنید ه خصوص اگر از قبل سه یا بیشتر آرگومان در این توابع داشته باشید.
-
-
-بد | خوب |
-
-
-
-```go
-// package db
-
-func Open(
- addr string,
- cache bool,
- logger *zap.Logger
-) (*Connection, error) {
- // ...
-}
-```
-
- |
-
-```go
-// package db
-
-type Option interface {
- // ...
-}
-
-func WithCache(c bool) Option {
- // ...
-}
-
-func WithLogger(log *zap.Logger) Option {
- // ...
-}
-
-// Open creates a connection.
-func Open(
- addr string,
- opts ...Option,
-) (*Connection, error) {
- // ...
-}
-```
-
- |
-
-
-پارامترهای کش و لاگر همیشه باید ارائه شوند، حتی اگر کاربر بخواهد از پیش فرض استفاده کند.
-
-```go
-db.Open(addr, db.DefaultCache, zap.NewNop())
-db.Open(addr, db.DefaultCache, log)
-db.Open(addr, false /* cache */, zap.NewNop())
-db.Open(addr, false /* cache */, log)
-```
-
- |
-
-گزینهها (Opptions) فقط در صورت نیاز ارائه میشوند.
-
-```go
-db.Open(addr)
-db.Open(addr, db.WithLogger(log))
-db.Open(addr, db.WithCache(false))
-db.Open(
- addr,
- db.WithCache(false),
- db.WithLogger(log),
-)
-```
-
- |
-
-
-روش پیشنهادی ما برای پیادهسازی این الگو استفاده از یک رابط (Interface) به نام `Option` است که یک متد خصوصی (unexported) را نگه میدارد و گزینهها (`options`) را در یک ساختار (struct) نیز خصوصی ثبت میکند.
-
-```go
-type options struct {
- cache bool
- logger *zap.Logger
-}
-
-type Option interface {
- apply(*options)
-}
-
-type cacheOption bool
-
-func (c cacheOption) apply(opts *options) {
- opts.cache = bool(c)
-}
-
-func WithCache(c bool) Option {
- return cacheOption(c)
-}
-
-type loggerOption struct {
- Log *zap.Logger
-}
-
-func (l loggerOption) apply(opts *options) {
- opts.logger = l.Log
-}
-
-func WithLogger(log *zap.Logger) Option {
- return loggerOption{Log: log}
-}
-
-// Open creates a connection.
-func Open(
- addr string,
- opts ...Option,
-) (*Connection, error) {
- options := options{
- cache: defaultCache,
- logger: zap.NewNop(),
- }
-
- for _, o := range opts {
- o.apply(&options)
- }
-
- // ...
-}
-```
-
-توجه داشته باشید که روشی برای پیادهسازی این الگو با استفاده از توابع بسته (closures) وجود دارد، اما ما باور داریم که الگوی بالا انعطاف بیشتری برای نویسندگان فراهم میکند و اشکالزدایی و آزمایش آن برای کاربران راحتتر است. به طور خاص، این الگو اجازه میدهد که گزینهها در تستها و موکها با یکدیگر مقایسه شوند، در مقابل توابع بسته که این امکان در آنها وجود ندارد. علاوه بر این، این الگو به گزینهها امکان پیادهسازی رابطهای دیگر را میدهد، از جمله `fmt.Stringer` که امکان نمایش رشتهای خوانا از گزینهها را فراهم میکند.
-
-همچنین ببینید،
-
-- [توابع خود ارجاعی و طراحی گزینهها (Self-referential functions and the design of options)](https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html)
-- [Functional options for friendly APIs](https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis)
-
-
-
-## بررسی و تمیز کردن (linting)
-
-مهمتر از هر چیز، اعمال یک استاندارد یکسان در کل پروژه است، نه استفاده از یک مجموعه خاص از ابزارهای بررسی کد.
-
-توصیه میکنیم حداقل از لینترهای زیر استفاده کنید، زیرا فکر میکنیم که این ابزارها به شناسایی مشکلات رایج کمک میکنند و همچنین یک استاندارد بالا برای کیفیت کد ایجاد میکنند بدون اینکه غیرضروری تجویز شوند:
-
-- [errcheck](https://github.com/kisielk/errcheck) برای اطمینان از رسیدگی به خطاها
-- [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports) برای قالب بندی کد و مدیریت واردات
-- [golint](https://github.com/golang/lint) برای اشاره به اشتباهات رایج استایل
-- [govet](https://golang.org/cmd/vet/) برای تجزیه و تحلیل کد برای اشتباهات رایج
-- [staticcheck](https://staticcheck.io/) برای انجام بررسی های مختلف آنالیز استاتیکی
-
-### Lint Runners
-
-ما توصیه میکنیم از [golangci-lint](https://github.com/golangci/golangci-lint) به عنوان ابزار اصلی برای اجرای عملیات lint در کد Go استفاده کنید، به دلیل عملکرد برتر آن در پروژههای بزرگ و قابلیت پیکربندی و استفاده از ابزارهای بررسی کد معتبر بسیاری به صورت همزمان. این مخزن (repo) یک فایل پیکربندی [.golangci.yml](https://github.com/uber-go/guide/blob/master/.golangci.yml) با ابزارهای بررسی کد پیشنهادی و تنظیمات راهنمایی شده را دارد.
-
-golangci-lint دارای [لینترهای مختلفی](https://golangci-lint.run/usage/linters/) برای استفاده است. لینترهای فوق به عنوان یک مجموعه پایه توصیه میشوند و ما تیمها را تشویق میکنیم که هر گونه لینتر اضافی را که برای پروژههایشان منطقی است اضافه کنند.
|