🦫 10 ترفند Go که هر برنامه نویس Go به آن نیاز دارد در جعبه ابزار خود!
Summarize this content to 400 words in Persian Lang Go مانند آن دوستی است که به نظر می رسد فوق العاده سرد است اما استعدادهای شگفت انگیزی در آستین خود دارد. در ظاهر ساده به نظر می رسد، اما چیزهای زیادی برای باز کردن وجود دارد. بنابراین، بیایید به ده ترفند Go برویم که هر توسعهدهندهای باید بداند – و من چند مثال میآورم تا آن را بیشتر روشن کنم!
1. بر قدرت گوروتین ها مسلط شوید
گوروتین ها مانند زنبورهای کارگر کوچکی هستند که چندوظیفه ای را نسیم می کنند. شما آنها را با رفتن کنار می گذارید و به آنها اجازه می دهید کارشان را انجام دهند. فقط فراموش نکنید که آنها را مدیریت کنید! sync.WaitGroup را وارد کنید، که به شما کمک میکند ردیابی کنید که همه برنامههای شما چه زمانی تمام شدهاند.
package main
import (
“fmt”
“sync”
“time”
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // Signals this worker is done
fmt.Printf(“Worker %d starting\n”, id)
time.Sleep(time.Second) // Simulate some work
fmt.Printf(“Worker %d done\n”, id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait() // Waits for all goroutines to finish
fmt.Println(“All workers done!”)
}
این را اجرا کنید، و خواهید دید که هر “کارگر” کار خود را انجام می دهد، سپس یک “همه کارگران تمام شد!” در پایان شیرین!
2. کانال: سلاح مخفی Go
کانالها راهی هستند که Go میگوید: «پیام را منتقل کن!» با chan، می توانید داده ها را بین گوروتین ها ارسال و دریافت کنید. اگر چت های زیادی دارید، از انتخاب برای گوش دادن همزمان به چندین کانال استفاده کنید. این مانند یک اپراتور تابلو برق برای گوروتین است.
package main
import “fmt”
func main() {
ch := make(chan string)
go func() {
ch <- “Hello from goroutine!”
}()
message := <-ch
fmt.Println(message) // Output: Hello from goroutine!
}
آن را اجرا کنید، و پیامی را خواهید دید که از گوروتین ما در تابع اصلی ظاهر می شود. بوم، ارتباط فوری!
3. آموزش defer برای Elegant Code Cleanup
کلمه کلیدی defer مانند این است که بگویید: “این را برای آخر ذخیره کنید!” برای پاکسازی عالی است—مانند بستن فایلها، آزاد کردن قفلها، و غیره. کد شما مرتب باقی میماند و احتمال اینکه مراحل پاکسازی را فراموش کنید کمتر است.
package main
import (
“fmt”
“os”
)
func main() {
file, err := os.Create(“example.txt”)
if err != nil {
panic(err)
}
defer file.Close() // This will always run last
fmt.Fprintln(file, “Hello, defer!”) // Write to the file
}
با defer، پرونده بدون توجه به هر اتفاقی بسته می شود، حتی اگر یک وحشت وجود داشته باشد. مثل داشتن یک شبکه ایمنی داخلی است!
4. مدیریت خطا مانند یک حرفه ای
Go استثناهای فانتزی را انجام نمی دهد. خطاها را به روشی ساده انجام می دهد. در هر مرحله خطاها را بررسی کنید، و زمانی که به پیام های سفارشی نیاز دارید، انواع خطاهای خود را ایجاد کنید!
package main
import (
“errors”
“fmt”
)
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New(“cannot divide by zero”)
}
return a / b, nil
}
func main() {
result, err := divide(4, 0)
if err != nil {
fmt.Println(“Error:”, err)
return
}
fmt.Println(“Result:”, result)
}
اگر در اینجا سعی کنید بر صفر تقسیم کنید، یک پیام خطای سفارشی زیبا دریافت خواهید کرد. بسیار تمیزتر از تصادفات مرموز!
5. رابط ها: بیش از انتزاعات
در Go، رابط ها در مورد رفتار هستند، نه وراثت. همه آنها در مورد “اگر به نظر می رسد مانند یک اردک و کوک مانند یک اردک، آن را یک اردک!” بنابراین آنها را ساده و متمرکز نگه دارید.
package main
import “fmt”
type Speaker interface {
Speak() string
}
type Dog struct{}
type Cat struct{}
func (d Dog) Speak() string { return “Woof!” }
func (c Cat) Speak() string { return “Meow!” }
func makeSound(s Speaker) {
fmt.Println(s.Speak())
}
func main() {
makeSound(Dog{}) // Output: Woof!
makeSound(Cat{}) // Output: Meow!
}
اکنون، هر ساختاری که متد Speak داشته باشد می تواند با makeSound کار کند. کد کمتر، انعطاف پذیری بیشتر!
6. با تگ های Struct بهینه سازی کنید
تگهای Struct مانند یادداشتهای چسبناک کوچکی در فیلدهای ساختار شما هستند. آنها به سیستم های خارجی می گویند که چگونه از فیلدهای شما استفاده کنند، خواه با JSON، XML یا پایگاه داده سروکار داشته باشید. json:”-” را برای نادیده گرفتن فیلدها اضافه کنید یا برای رد شدن از فیلدهای خالی آن را حذف کنید.
package main
import (
“encoding/json”
“fmt”
)
type Person struct {
Name string `json:”name”`
Age int `json:”age,omitempty”` // Will skip if zero
Email string `json:”-“`
}
func main() {
p := Person{Name: “John”, Age: 0, Email: “john@example.com”}
data, _ := json.Marshal(p)
fmt.Println(string(data)) // Output: {“name”:”John”}
}
در اینجا، ایمیل نادیده گرفته می شود و سن به دلیل صفر بودن نشان داده نمی شود. ایده آل برای JSON تمیزتر و سبک تر!
7. محک زدن برای عملکرد رعد و برق
آیا می خواهید بدانید سرعت کد شما چقدر است؟ بسته آزمایشی Go دارای بنچمارک داخلی است. میتوانید گلوگاهها را پیدا کنید و قبل از شروع به کار بهینهسازی کنید.
package main
import (
“testing”
)
func add(a, b int) int {
return a + b
}
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
add(1, 2)
}
}
Run go test-bench . و بازخورد فوری در مورد عملکرد دریافت کنید. حتی تغییرات کوچک می تواند تفاوت زیادی در سرعت ایجاد کند!
8. برش های اهرمی برای آرایه های پویا
Slices آرایه های پویا Go هستند، اما با کمی انعطاف بیشتر. میتوانید با افزودن آنها را گسترش دهید و فضای اضافی را از قبل اختصاص دهید تا کارها کارآمد باشند.
package main
import “fmt”
func main() {
numbers := make([]int, 0, 5) // Capacity of 5
numbers = append(numbers, 1, 2, 3)
fmt.Println(numbers) // Output: [1 2 3]
fmt.Println(cap(numbers)) // Output: 5
}
با اضافه کردن عناصر، برش ها رشد می کنند، اما تخصیص ظرفیت از قبل (مانند اینجا با 5) کمی سرعت را به شما افزایش می دهد.
9. نقشه مانند یک رئیس
نقشه ها سریع و آسان هستند، اما از نظر رشته ای ایمن نیستند. بنابراین اگر از آنها با گوروتینها استفاده میکنید، آنها را در یک sync.RWMutex بپیچید تا از وحشت و باگهای عجیب و غریب جلوگیری کنید.
package main
import (
“fmt”
“sync”
)
func main() {
m := make(map[string]int)
var mu sync.RWMutex
// Write
mu.Lock()
m[“key”] = 42
mu.Unlock()
// Read
mu.RLock()
fmt.Println(m[“key”]) // Output: 42
mu.RUnlock()
}
با sync.RWMutex، می توانید با خیال راحت نقشه خود را در یک محیط همزمان بخوانید و بنویسید. صاف و ایمن!
10. مرتب سازی مانند یک حرفه ای با بسته بندی داخلی Go
در Go، سفارش برش ها بسیار آسان است. آیا نیاز به مرتب سازی اعداد صحیح، رشته ها یا هر قطعه اساسی دیگر دارید؟ کتابخانه استاندارد مرتب سازی توابع آماده ای برای این کار دارد! اما بهترین بخش، انعطاف پذیری سفارش برش های ساختارهای سفارشی با استفاده از sort.Slice است. می توانید صعودی، نزولی یا با هر معیاری که می خواهید مرتب کنید!
مثال: ترتیب رشته ها، اعداد و ساختارهای سفارشی
بیایید با یک مثال ساده شروع کنیم: مرتب سازی اعداد صحیح و رشته ها.
package main
import (
“fmt”
“sort”
)
func main() {
numbers := []int{5, 2, 7, 3, 9}
sort.Ints(numbers)
fmt.Println(“Sorted Numbers:”, numbers) // Output: [2 3 5 7 9]
words := []string{“banana”, “apple”, “cherry”}
sort.Strings(words)
fmt.Println(“Sorted Words:”, words) // Output: [apple banana cherry]
}
دیدی که؟ توابع sort.Ints و sort.strings همه چیز را به سرعت مرتب می کنند.
ساختارهای مرتب سازی: نمونه ای از مرتب سازی سفارشی با sort.Slice
برای چیزهای پیشرفته تر، مانند مرتب کردن لیستی از ساختارها، تابع sort.Slice به شما امکان می دهد معیارهای مرتب سازی را تعریف کنید. در اینجا یک مثال با لیستی از افراد، به ترتیب سن آورده شده است.
package main
import (
“fmt”
“sort”
)
type Person struct {
Name string
Age int
}
func main() {
people := []Person{
{“Alice”, 30},
{“Bob”, 25},
{“Charlie”, 35},
}
// Sort by age
sort.Slice(people, func(i, j int) bool {
return people[i].Age < people[j].Age // increasing age
})
fmt.Println(“Sorted by Age:”, people)
// Sort by name (alphabetical)
sort.Slice(people, func(i, j int) bool {
return people[i].Name < people[j].Name
})
fmt.Println(“Sorted by Name:”, people)
}
مهم نیست که ساختار دادههای شما چقدر پیچیده است، sort.Slice به شما امکان میدهد آنها را به روشی شخصیسازی شده مرتب کنید، فقط با تغییر عملکردی که دو مورد را با هم مقایسه میکند. این امر زندگی را تا حد زیادی ساده می کند و از نیاز به اجرای الگوریتم های مرتب سازی از ابتدا جلوگیری می کند.
هک مبارک، Gophers! 🦫
Go مانند آن دوستی است که به نظر می رسد فوق العاده سرد است اما استعدادهای شگفت انگیزی در آستین خود دارد. در ظاهر ساده به نظر می رسد، اما چیزهای زیادی برای باز کردن وجود دارد. بنابراین، بیایید به ده ترفند Go برویم که هر توسعهدهندهای باید بداند – و من چند مثال میآورم تا آن را بیشتر روشن کنم!
1. بر قدرت گوروتین ها مسلط شوید
گوروتین ها مانند زنبورهای کارگر کوچکی هستند که چندوظیفه ای را نسیم می کنند. شما آنها را با رفتن کنار می گذارید و به آنها اجازه می دهید کارشان را انجام دهند. فقط فراموش نکنید که آنها را مدیریت کنید! sync.WaitGroup را وارد کنید، که به شما کمک میکند ردیابی کنید که همه برنامههای شما چه زمانی تمام شدهاند.
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // Signals this worker is done
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second) // Simulate some work
fmt.Printf("Worker %d done\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait() // Waits for all goroutines to finish
fmt.Println("All workers done!")
}
این را اجرا کنید، و خواهید دید که هر “کارگر” کار خود را انجام می دهد، سپس یک “همه کارگران تمام شد!” در پایان شیرین!
2. کانال: سلاح مخفی Go
کانالها راهی هستند که Go میگوید: «پیام را منتقل کن!» با chan، می توانید داده ها را بین گوروتین ها ارسال و دریافت کنید. اگر چت های زیادی دارید، از انتخاب برای گوش دادن همزمان به چندین کانال استفاده کنید. این مانند یک اپراتور تابلو برق برای گوروتین است.
package main
import "fmt"
func main() {
ch := make(chan string)
go func() {
ch <- "Hello from goroutine!"
}()
message := <-ch
fmt.Println(message) // Output: Hello from goroutine!
}
آن را اجرا کنید، و پیامی را خواهید دید که از گوروتین ما در تابع اصلی ظاهر می شود. بوم، ارتباط فوری!
3. آموزش defer برای Elegant Code Cleanup
کلمه کلیدی defer مانند این است که بگویید: “این را برای آخر ذخیره کنید!” برای پاکسازی عالی است—مانند بستن فایلها، آزاد کردن قفلها، و غیره. کد شما مرتب باقی میماند و احتمال اینکه مراحل پاکسازی را فراموش کنید کمتر است.
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Create("example.txt")
if err != nil {
panic(err)
}
defer file.Close() // This will always run last
fmt.Fprintln(file, "Hello, defer!") // Write to the file
}
با defer، پرونده بدون توجه به هر اتفاقی بسته می شود، حتی اگر یک وحشت وجود داشته باشد. مثل داشتن یک شبکه ایمنی داخلی است!
4. مدیریت خطا مانند یک حرفه ای
Go استثناهای فانتزی را انجام نمی دهد. خطاها را به روشی ساده انجام می دهد. در هر مرحله خطاها را بررسی کنید، و زمانی که به پیام های سفارشی نیاز دارید، انواع خطاهای خود را ایجاد کنید!
package main
import (
"errors"
"fmt"
)
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("cannot divide by zero")
}
return a / b, nil
}
func main() {
result, err := divide(4, 0)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Result:", result)
}
اگر در اینجا سعی کنید بر صفر تقسیم کنید، یک پیام خطای سفارشی زیبا دریافت خواهید کرد. بسیار تمیزتر از تصادفات مرموز!
5. رابط ها: بیش از انتزاعات
در Go، رابط ها در مورد رفتار هستند، نه وراثت. همه آنها در مورد “اگر به نظر می رسد مانند یک اردک و کوک مانند یک اردک، آن را یک اردک!” بنابراین آنها را ساده و متمرکز نگه دارید.
package main
import "fmt"
type Speaker interface {
Speak() string
}
type Dog struct{}
type Cat struct{}
func (d Dog) Speak() string { return "Woof!" }
func (c Cat) Speak() string { return "Meow!" }
func makeSound(s Speaker) {
fmt.Println(s.Speak())
}
func main() {
makeSound(Dog{}) // Output: Woof!
makeSound(Cat{}) // Output: Meow!
}
اکنون، هر ساختاری که متد Speak داشته باشد می تواند با makeSound کار کند. کد کمتر، انعطاف پذیری بیشتر!
6. با تگ های Struct بهینه سازی کنید
تگهای Struct مانند یادداشتهای چسبناک کوچکی در فیلدهای ساختار شما هستند. آنها به سیستم های خارجی می گویند که چگونه از فیلدهای شما استفاده کنند، خواه با JSON، XML یا پایگاه داده سروکار داشته باشید. json:”-” را برای نادیده گرفتن فیلدها اضافه کنید یا برای رد شدن از فیلدهای خالی آن را حذف کنید.
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age,omitempty"` // Will skip if zero
Email string `json:"-"`
}
func main() {
p := Person{Name: "John", Age: 0, Email: "john@example.com"}
data, _ := json.Marshal(p)
fmt.Println(string(data)) // Output: {"name":"John"}
}
در اینجا، ایمیل نادیده گرفته می شود و سن به دلیل صفر بودن نشان داده نمی شود. ایده آل برای JSON تمیزتر و سبک تر!
7. محک زدن برای عملکرد رعد و برق
آیا می خواهید بدانید سرعت کد شما چقدر است؟ بسته آزمایشی Go دارای بنچمارک داخلی است. میتوانید گلوگاهها را پیدا کنید و قبل از شروع به کار بهینهسازی کنید.
package main
import (
"testing"
)
func add(a, b int) int {
return a + b
}
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
add(1, 2)
}
}
Run go test-bench . و بازخورد فوری در مورد عملکرد دریافت کنید. حتی تغییرات کوچک می تواند تفاوت زیادی در سرعت ایجاد کند!
8. برش های اهرمی برای آرایه های پویا
Slices آرایه های پویا Go هستند، اما با کمی انعطاف بیشتر. میتوانید با افزودن آنها را گسترش دهید و فضای اضافی را از قبل اختصاص دهید تا کارها کارآمد باشند.
package main
import "fmt"
func main() {
numbers := make([]int, 0, 5) // Capacity of 5
numbers = append(numbers, 1, 2, 3)
fmt.Println(numbers) // Output: [1 2 3]
fmt.Println(cap(numbers)) // Output: 5
}
با اضافه کردن عناصر، برش ها رشد می کنند، اما تخصیص ظرفیت از قبل (مانند اینجا با 5) کمی سرعت را به شما افزایش می دهد.
9. نقشه مانند یک رئیس
نقشه ها سریع و آسان هستند، اما از نظر رشته ای ایمن نیستند. بنابراین اگر از آنها با گوروتینها استفاده میکنید، آنها را در یک sync.RWMutex بپیچید تا از وحشت و باگهای عجیب و غریب جلوگیری کنید.
package main
import (
"fmt"
"sync"
)
func main() {
m := make(map[string]int)
var mu sync.RWMutex
// Write
mu.Lock()
m["key"] = 42
mu.Unlock()
// Read
mu.RLock()
fmt.Println(m["key"]) // Output: 42
mu.RUnlock()
}
با sync.RWMutex، می توانید با خیال راحت نقشه خود را در یک محیط همزمان بخوانید و بنویسید. صاف و ایمن!
10. مرتب سازی مانند یک حرفه ای با بسته بندی داخلی Go
در Go، سفارش برش ها بسیار آسان است. آیا نیاز به مرتب سازی اعداد صحیح، رشته ها یا هر قطعه اساسی دیگر دارید؟ کتابخانه استاندارد مرتب سازی توابع آماده ای برای این کار دارد! اما بهترین بخش، انعطاف پذیری سفارش برش های ساختارهای سفارشی با استفاده از sort.Slice است. می توانید صعودی، نزولی یا با هر معیاری که می خواهید مرتب کنید!
مثال: ترتیب رشته ها، اعداد و ساختارهای سفارشی
بیایید با یک مثال ساده شروع کنیم: مرتب سازی اعداد صحیح و رشته ها.
package main
import (
"fmt"
"sort"
)
func main() {
numbers := []int{5, 2, 7, 3, 9}
sort.Ints(numbers)
fmt.Println("Sorted Numbers:", numbers) // Output: [2 3 5 7 9]
words := []string{"banana", "apple", "cherry"}
sort.Strings(words)
fmt.Println("Sorted Words:", words) // Output: [apple banana cherry]
}
دیدی که؟ توابع sort.Ints و sort.strings همه چیز را به سرعت مرتب می کنند.
ساختارهای مرتب سازی: نمونه ای از مرتب سازی سفارشی با sort.Slice
برای چیزهای پیشرفته تر، مانند مرتب کردن لیستی از ساختارها، تابع sort.Slice به شما امکان می دهد معیارهای مرتب سازی را تعریف کنید. در اینجا یک مثال با لیستی از افراد، به ترتیب سن آورده شده است.
package main
import (
"fmt"
"sort"
)
type Person struct {
Name string
Age int
}
func main() {
people := []Person{
{"Alice", 30},
{"Bob", 25},
{"Charlie", 35},
}
// Sort by age
sort.Slice(people, func(i, j int) bool {
return people[i].Age < people[j].Age // increasing age
})
fmt.Println("Sorted by Age:", people)
// Sort by name (alphabetical)
sort.Slice(people, func(i, j int) bool {
return people[i].Name < people[j].Name
})
fmt.Println("Sorted by Name:", people)
}
مهم نیست که ساختار دادههای شما چقدر پیچیده است، sort.Slice به شما امکان میدهد آنها را به روشی شخصیسازی شده مرتب کنید، فقط با تغییر عملکردی که دو مورد را با هم مقایسه میکند. این امر زندگی را تا حد زیادی ساده می کند و از نیاز به اجرای الگوریتم های مرتب سازی از ابتدا جلوگیری می کند.
هک مبارک، Gophers! 🦫