Gopher Academy
Відкрити в Telegram
🕸 Gopher Academy 🔷interview golang https://github.com/mrbardia72/Go-Interview-Questions-And-Answers حمایت مالی: https://www.coffeete.ir/mrbardia72 ادمین: @mrbardia72
Показати більше3 832
Підписники
+324 години
+57 днів
+1930 днів
Триває завантаження даних...
Схожі канали
Хмара тегів
Вхідні та вихідні згадування
---
---
---
---
---
---
Залучення підписників
вересень '26
вересень '26
+5
в 6 каналах
серпень '26
+69
в 4 каналах
Get PRO
липень '26
+58
в 0 каналах
Get PRO
червень '26
+67
в 0 каналах
Get PRO
травень '26
+45
в 0 каналах
Get PRO
квітень '26
+24
в 5 каналах
Get PRO
березень '26
+10
в 0 каналах
Get PRO
лютий '26
+79
в 1 каналах
Get PRO
січень '26
+29
в 8 каналах
Get PRO
грудень '25
+103
в 7 каналах
Get PRO
листопад '25
+72
в 0 каналах
Get PRO
жовтень '25
+598
в 1 каналах
Get PRO
вересень '25
+61
в 6 каналах
Get PRO
серпень '25
+84
в 9 каналах
Get PRO
липень '25
+99
в 10 каналах
Get PRO
червень '25
+47
в 7 каналах
Get PRO
травень '25
+45
в 1 каналах
Get PRO
квітень '25
+63
в 8 каналах
Get PRO
березень '25
+90
в 8 каналах
Get PRO
лютий '25
+91
в 3 каналах
Get PRO
січень '25
+96
в 3 каналах
Get PRO
грудень '24
+113
в 7 каналах
Get PRO
листопад '24
+94
в 2 каналах
Get PRO
жовтень '24
+116
в 4 каналах
Get PRO
вересень '24
+138
в 4 каналах
Get PRO
серпень '24
+101
в 2 каналах
Get PRO
липень '24
+187
в 5 каналах
Get PRO
червень '24
+173
в 11 каналах
Get PRO
травень '24
+301
в 2 каналах
Get PRO
квітень '24
+185
в 3 каналах
Get PRO
березень '24
+172
в 3 каналах
Get PRO
лютий '24
+154
в 0 каналах
Get PRO
січень '24
+207
в 0 каналах
Get PRO
грудень '23
+215
в 4 каналах
Get PRO
листопад '23
+77
в 2 каналах
Get PRO
жовтень '23
+79
в 0 каналах
Get PRO
вересень '23
+70
в 0 каналах
Get PRO
серпень '23
+108
в 0 каналах
Get PRO
липень '23
+68
в 0 каналах
Get PRO
червень '23
+106
в 0 каналах
Get PRO
травень '23
+211
в 0 каналах
Get PRO
квітень '23
+142
в 0 каналах
Get PRO
березень '23
+56
в 0 каналах
Get PRO
лютий '23
+70
в 0 каналах
Get PRO
січень '23
+62
в 0 каналах
Get PRO
грудень '22
+14
в 0 каналах
Get PRO
листопад '22
+24
в 0 каналах
Get PRO
жовтень '22
+15
в 0 каналах
Get PRO
вересень '22
+78
в 0 каналах
Get PRO
серпень '22
+47
в 0 каналах
Get PRO
липень '22
+36
в 0 каналах
Get PRO
червень '22
+26
в 0 каналах
Get PRO
травень '22
+27
в 0 каналах
Get PRO
квітень '22
+14
в 0 каналах
Get PRO
березень '22
+21
в 0 каналах
Get PRO
лютий '22
+13
в 0 каналах
Get PRO
січень '22
+24
в 0 каналах
Get PRO
грудень '21
+51
в 0 каналах
Get PRO
листопад '21
+107
в 0 каналах
Get PRO
жовтень '21
+13
в 0 каналах
Get PRO
вересень '21
+31
в 0 каналах
Get PRO
серпень '21
+29
в 0 каналах
Get PRO
липень '21
+41
в 0 каналах
Get PRO
червень '21
+29
в 0 каналах
Get PRO
травень '21
+26
в 0 каналах
Get PRO
квітень '21
+49
в 0 каналах
Get PRO
березень '21
+376
в 0 каналах
| Дата | Залучення підписників | Згадування | Канали | |
| 02 вересня | +1 | |||
| 01 вересня | +4 |
Дописи каналу
| 2 | معرفی ویژگی های گو 1.26 تکمیل شد☝️📣🕊 | 318 |
| 3 | 🎖Modernized go fix
در طول سالها، دستور go fix به مجموعهای کمرمق و تقریباً فراموششده از rewriteهایی برای قابلیتهای بسیار قدیمی Go تبدیل شده بود. اما در Go 1.26، این ابزار عملاً دوباره متولد شده است.
نسخه جدید go fix با استفاده از Go Analysis Framework، یعنی همان زیرساختی که go vet از آن استفاده میکند، بازنویسی شده است.
با اینکه go fix و go vet اکنون زیرساخت مشترکی دارند، هدف آنها متفاوت است:
این go vet برای گزارش مشکلات است. تحلیلگرهای آن خطاها یا الگوهای مشکلدار واقعی را شناسایی میکنند؛ اما لزوماً برای همه آنها fix ارائه نمیدهند و fixهای پیشنهادی نیز همیشه ایمن برای اعمال خودکار نیستند.
این دستور go fix عمدتاً برای مدرنسازی کد و استفاده از قابلیتهای جدیدتر زبان و کتابخانههای Go است. تحلیلگرهای آن fixهایی تولید میکنند که همیشه ایمن برای اعمال خودکار هستند، هرچند لزوماً به این معنا نیست که کد فعلی مشکلی دارد.
ساختار کلی استفاده از ابزار:
usage: go fix [build flags] [-fixtool prog] [fix flags] [packages]
Fix runs the Go fix tool (cmd/fix) on the named packages
and applies suggested fixes.
It supports these flags:
-diff
instead of applying each fix, print the patch as a unified diff
The -fixtool=prog flag selects a different analysis tool with
alternative or additional fixers.
بهصورت پیشفرض، go fix مجموعه کاملی از تحلیلگرها را اجرا میکند که در حال حاضر بیش از ۲۰ تحلیلگر دارد.
اگر بخواهید فقط تحلیلگرهای مشخصی را اجرا کنید، میتوانید از فلگ -NAME استفاده کنید. همچنین با -NAME=false میتوانید یک تحلیلگر مشخص را غیرفعال کرده و سایر تحلیلگرها را اجرا کنید.
برای مثال، فقط تحلیلگر forvar را فعال میکنیم:
go fix -forvar .
یا تمام تحلیلگرها را اجرا میکنیم، بهجز omitzero:
go fix -omitzero=false .
در حال حاضر راهی برای غیرفعالکردن یک تحلیلگر مشخص فقط برای فایل یا بخش خاصی از کد وجود ندارد.
یک مثال واقعی
یکی از تحلیلگرهای go fix میتواند حلقههایی را که صرفاً برای جستوجوی یک مقدار در slice استفاده شدهاند، با slices.Contains یا slices.ContainsFunc جایگزین کند.
قبل از اجرای go fix:
func find(s []int, x int) bool {
for _, v := range s {
if x == v {
return true
}
}
return false
}
بعد از اجرای go fix:
func find(s []int, x int) bool {
return slices.Contains(s, x)
}
یعنی ابزار میتواند الگوهای قدیمی و verbose را تشخیص دهد و آنها را به APIهای مدرنتر Go تبدیل کند، بدون اینکه رفتار برنامه تغییر کند.
این تغییر، go fix را از یک ابزار legacy به بخشی جدیتر از toolchain مدرن Go برای migration و modernization کد تبدیل میکند.
نکته مهم این است که go fix جای go vet را نمیگیرد؛ این دو ابزار اهداف متفاوتی دارند:
go vet → پیدا کردن مشکلات
go fix → مدرنسازی ایمن کد | 278 |
| 4 | 🎖Test Artifacts
این Test artifact به فایلهایی گفته میشود که در جریان اجرای test یا benchmark تولید میشوند؛ مانند:
execution log
memory dump
analysis report
و سایر خروجیهای موردنیاز برای debugging
این فایلها بهخصوص هنگام اجرای تستها در محیطهای remote مانند CI اهمیت زیادی دارند؛ جایی که معمولاً امکان step-by-step debug کردن کد وجود ندارد.
تا قبل از Go 1.26، framework و tooling تست Go پشتیبانی مناسبی از test artifacts نداشتند.
در Go 1.26 این قابلیت اضافه شده است.
ArtifactDir
سه API جدید:
T.ArtifactDir
B.ArtifactDir
F.ArtifactDir
یک directory در اختیار تست قرار میدهند که میتوانید فایلهای خروجی تست را داخل آن ذخیره کنید.
مثلاً:
func TestFunc(t *testing.T) {
dir := t.ArtifactDir()
logFile := filepath.Join(dir, "app.log")
content := []byte(
"Loading user_id=123...\nERROR: Connection failed\n",
)
os.WriteFile(logFile, content, 0644)
t.Log("Saved app.log")
}
در این مثال، تست یک فایل app.log در artifact directory خودش ایجاد میکند.
نگهداری Artifactها با go test
اگر go test را همراه با flag جدید -artifacts اجرا کنید، artifact directory داخل output directory قرار میگیرد.
مثلاً:
go test -v -artifacts -outputdir=/tmp/output
اگر -outputdir مشخص نشده باشد، از directory فعلی استفاده میشود.
نکتهٔ جالب این است که اولین بار که ArtifactDir در یک test فراخوانی شود، مسیر directory در test log ثبت میشود.
این موضوع در محیطهایی مثل CI بسیار کاربردی است؛ چون بعد از شکست تست، مسیر artifactها در log مشخص خواهد بود.
اگر -artifacts استفاده نشود چه اتفاقی میافتد؟
در صورتی که -artifacts را مشخص نکنید، artifactها در یک temporary directory ذخیره میشوند.
این directory پس از پایان test حذف خواهد شد.
بنابراین اگر میخواهید artifactها برای بررسی بعدی باقی بمانند، باید تست را با -artifacts اجرا کنید.
این Artifact مستقل برای هر Test و Subtest
هر test یا subtest در هر package، یک artifact directory اختصاصی و unique دارد.
مثلاً:
func TestFunc(t *testing.T) {
t.ArtifactDir()
t.Run("subtest 1", func(t *testing.T) {
t.ArtifactDir()
})
t.Run("subtest 2", func(t *testing.T) {
t.ArtifactDir()
})
}
و artifactهای subtest 1 و subtest 2 داخل artifact directory تست والد قرار نمیگیرند.
تمام artifact directoryهای مربوط به یک package در یک سطح ایجاد میشوند.
ساختار معمول مسیر به شکل زیر است:
<output dir>/_artifacts/<test package>/<test name>/<random>
البته اگر این مسیر نتواند بهشکل امن به یک local file path تبدیل شود، Go از ساختار سادهتری استفاده میکند:
<output dir>/_artifacts/<random>
یک نکتهٔ مهم دربارهٔ ArtifactDir
اگر ArtifactDir را چند بار در یک test یا subtest فراخوانی کنید، هر بار همان directory برگردانده میشود.
یعنی:
dir1 := t.ArtifactDir()
dir2 := t.ArtifactDir()
// dir1 == dir2
این رفتار باعث میشود بخشهای مختلف یک test بتوانند artifactهای خود را بدون نگرانی از ایجاد directoryهای متعدد، در یک محل مشترک ذخیره کنند.
کاربرد عملی در CI
یکی از کاربردهای مهم این قابلیت، نگهداری اطلاعاتی است که فقط هنگام failure تولید میشوند:
Test
│
├── app.log
├── request.json
├── response.json
├── memory.dump
└── analysis.txt
و سپس CI میتواند کل _artifacts را بهعنوان build/test artifact ذخیره کند.
در نتیجه، Go 1.26 یک primitive استاندارد برای جمعآوری و نگهداری خروجیهای diagnostic تستها فراهم میکند؛ قابلیتی که در پروژههای بزرگ و بهخصوص pipelineهای CI/CD میتواند debugging failureها را بسیار سادهتر کند. | 164 |
| 5 | 🎖Multiple Log Handlers
در این پکیج یعنی package log/slog که در Go 1.21 معرفی شد، یک راهکار logging قابلاعتماد و مناسب production ارائه میکند.
از زمان معرفی slog، بسیاری از پروژهها بهجای استفاده از logging packageهای شخص ثالث، به این package مهاجرت کردهاند.
اما یک قابلیت مهم در آن وجود نداشت:
امکان ارسال یک log record به چند Handler مختلف.
برای مثال، ممکن بود بخواهید یک رکورد لاگ همزمان در stdout و یک log file نوشته شود.
درGo 1.26 با معرفی MultiHandler این مشکل را حل میکند.
با MultiHandler، interface استاندارد slog.Handler را پیادهسازی میکند و هر log record را به تمام Handlerهایی که برای آن تنظیم شدهاند ارسال میکند.
ارسال لاگ به stdout
ابتدا میتوانیم یک Handler برای stdout ایجاد کنیم:
stdoutHandler := slog.NewTextHandler(os.Stdout, nil)
و یک Handler دیگر برای نوشتن لاگ در فایل:
const flags = os.O_CREATE | os.O_WRONLY | os.O_APPEND
file, _ := os.OpenFile("/tmp/app.log", flags, 0644)
defer file.Close()
fileHandler := slog.NewJSONHandler(file, nil)
حالا هر دو Handler را با MultiHandler ترکیب میکنیم:
// MultiHandler that writes to both stdout and app.log.
multiHandler := slog.NewMultiHandler(stdoutHandler, fileHandler)
logger := slog.New(multiHandler)
// Log a sample message.
logger.Info("login",
slog.String("name", "whoami"),
slog.Int("id", 42),
)
در نتیجه، یک log record واحد در هر دو مقصد نوشته میشود.
در stdout، خروجی Text format خواهد بود:
time=2026-09-01T04:44:48.124Z level=INFO msg=login name=whoami id=42
و در فایل، چون از JSONHandler استفاده کردهایم:
{
"time": "2026-09-01T04:44:48.124493008Z",
"level": "INFO",
"msg": "login",
"name": "whoami",
"id": 42
}
رفتار MultiHandler در برابر خطا
وقتی MultiHandler یک log record دریافت میکند، آن را به هر Handler فعال، یکی پس از دیگری، ارسال میکند.
نکتهٔ مهم این است که اگر یکی از Handlerها error برگرداند، MultiHandler متوقف نمیشود.
در عوض، خطاهای تمام Handlerها را با استفاده از:
errors.Join
ترکیب میکند.
مثلاً:
hInfo := slog.NewTextHandler(
os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo},
)
hErrorsOnly := slog.NewTextHandler(
os.Stdout, &slog.HandlerOptions{Level: slog.LevelError},
)
hBroken := &BrokenHandler{
Handler: hInfo,
err: fmt.Errorf("broken handler"),
}
handler := slog.NewMultiHandler(hBroken, hInfo, hErrorsOnly)
rec := slog.NewRecord(
time.Now(),
slog.LevelInfo,
"hello",
0,
)
// Calls hInfo and hBroken, skips hErrorsOnly.
// Returns an error from hBroken.
err := handler.Handle(context.Background(), rec)
fmt.Println(err)
در این مثال:
hBroken فعال است، بنابراین فراخوانی میشود و error برمیگرداند.
hInfo نیز فعال است و log را پردازش میکند.
hErrorsOnly برای INFO فعال نیست، بنابراین skip میشود.
MultiHandler با وجود error مربوط به hBroken اجرای سایر Handlerهای فعال را متوقف نمیکند.
خروجی:
time=2026-09-01T04:44:52.605Z level=INFO msg=hello
broken handler
این رفتار برای production logging اهمیت زیادی دارد؛ خرابی یک sink نباید الزاماً باعث شود سایر مسیرهای logging نیز از کار بیفتند.
بررسی فعال بودن Handlerها
متد Enabled مشخص میکند که آیا حداقل یکی از Handlerهای تنظیمشده، یک log record با level مشخص را فعال میداند یا خیر.
مثلاً:
hInfo := slog.NewTextHandler(
os.Stdout,
&slog.HandlerOptions{Level: slog.LevelInfo},
)
hErrors := slog.NewTextHandler(
os.Stdout,
&slog.HandlerOptions{Level: slog.LevelError},
)
handler := slog.NewMultiHandler(hInfo, hErrors)
// hInfo is enabled.
enabled := handler.Enabled(
context.Background(),
slog.LevelInfo,
)
fmt.Println(enabled)
خروجی:
true
چون hInfo برای level INFO فعال است، MultiHandler.Enabled نیز true برمیگرداند.
در کنار Enabled و Handle، متدهای دیگر مانند:
WithAttr
WithGroup
نیز رفتار مشابهی دارند و متد متناظر را روی تمام Handlerهای فعال اجرا میکنند.
در نتیجه، MultiHandler یک abstraction ساده برای ساخت pipelineهای logging چندمقصدی فراهم میکند؛ مثلاً:
┌── stdout / TextHandler
│
Logger ──> MultiHandler ──> file / JSONHandler
│
└── remote / custom Handler | 70 |
| 6 | 🎖Optimized io.ReadAll
در نسخههای قبلی Go، io.ReadAll هنگام بزرگتر شدن result slice تا رسیدن به اندازهٔ نهایی داده، مقدار قابلتوجهی حافظهٔ میانی تخصیص میداد.
مشکل از نحوهٔ رشد slice ناشی میشد: با افزایش حجم داده، slice چندین بار resize میشد و در این فرآیند allocation و کپیهای اضافی اتفاق میافتاد.
در Go 1.26، implementation تغییر کرده است.
اکنون io.ReadAll از sliceهای میانی با اندازهٔ رشد نمایی (exponential growth) استفاده میکند و در پایان، دادهها را یکبار داخل یک slice نهایی با اندازهٔ دقیق کپی میکند.
در نتیجه، io.ReadAll تقریباً دو برابر سریعتر شده و برای ورودی حدود 65 KiB تقریباً نیمی از حافظهٔ قبلی را مصرف میکند.
برای ورودیهای بزرگتر، بهبود حتی بیشتر است.
نتایج geomean benchmark بین implementation قدیمی و جدید:
│ old │ new vs base │
sec/op 132.2µ 66.32µ -49.83%
B/op 645.4Ki 324.6Ki -49.70%
final-capacity 178.3k 151.3k -15.10%
excess-ratio 1.216 1.033 -15.10%
یعنی در این benchmark:
زمان اجرا تقریباً 50٪ کاهش پیدا کرده است.
حافظهٔ تخصیصیافته تقریباً 50٪ کاهش یافته است.
ظرفیت نهایی slice نیز حدود 15٪ کمتر شده است.
نسبت ظرفیت اضافی (excess-ratio) از 1.216 به 1.033 رسیده است.
چرا final slice با اندازهٔ دقیق مهم است؟
ممکن است دادهای که توسط io.ReadAll خوانده میشود، برای مدت طولانی در حافظه باقی بماند.
در implementation قدیمی، backing array مربوط به slice ممکن بود ظرفیت بیشتری از مقدار واقعی داده داشته باشد.
مثلاً اگر دادهٔ واقعی 100 KiB باشد اما backing array ظرفیت 120 KiB داشته باشد، آن 20 KiB اضافه تا زمانی که backing array زنده است، عملاً حافظهٔ بلااستفادهای خواهد بود.
و implementation جدید در پایان یک slice با ظرفیت تقریباً دقیق موردنیاز ایجاد میکند و بنابراین مقدار حافظهٔ بلااستفاده را کاهش میدهد.
این موضوع مخصوصاً در سرویسهایی که io.ReadAll برای خواندن payloadهای بزرگ یا دادههایی با lifetime طولانی استفاده میشود، اهمیت دارد.
همانند بهینهسازی fmt.Errorf، خواندن CL مربوط به این تغییر را هم توصیه میکنم؛ توضیحات implementation بسیار دقیق و ارزشمند هستند. | 85 |
| 7 | 🎖Optimized fmt.Errorf
مدتهاست یک نکتهٔ رایج در بحث performance مربوط به error handling در Go وجود دارد:
استفاده از:
fmt.Errorf("x")
برای ساختن یک string ساده، معمولاً allocationهای بیشتری نسبت به:
errors.New("x")
ایجاد میکرد.
به همین دلیل، بعضی توسعهدهندگان پیشنهاد میکردند وقتی نیازی به formatting نداریم، fmt.Errorf را با errors.New جایگزین کنیم.
اما تیم Go با این دیدگاه موافق نیست.
اقای Russ Cox در این رابطه میگوید استفاده از:
fmt.Errorf("foo")
کاملاً مناسب است؛ بهخصوص در پروژهای که errorها بهصورت یکپارچه با fmt.Errorf ساخته میشوند. اینکه برنامهنویس مجبور باشد صرفاً بر اساس نوع argument بین دو function مختلف انتخاب کند، عملاً noise غیرضروری به کد اضافه میکند.
در واقعه Go 1.26 این بحث را تا حد زیادی حل کرده است.
و fmt.Errorf اکنون بسیار کمهزینهتر است
اگر string ورودی هیچ formatting نداشته باشد، fmt.Errorf اکنون allocation بسیار کمتری انجام میدهد و در حالت کلی از نظر allocation با errors.New قابلمقایسه است.
بهطور مشخص:
برای non-escaping error:
قبل: 2 allocations
اکنون: 0 allocations
برای escaping error:
قبل: 2 allocations
اکنون: 1 allocation
این دقیقاً با تعداد allocationهای errors.New در همین دو حالت مطابقت دارد.
مثلاً:
_ = fmt.Errorf("foo") // non-escaping
sink = fmt.Errorf("foo") // escaping
از نظر هزینهٔ CPU نیز فاصله بسیار کمتر شده است.
برای یک escaping error، قبلاً تقریباً:
fmt.Errorf ≈ 64 ns
errors.New ≈ 21 ns
بود؛ اما اکنون:
fmt.Errorf ≈ 25 ns
errors.New ≈ 21 ns
است.
یعنی در مواردی که formatting واقعاً لازم نیست، دیگر اختلاف performance قابلتوجهی بین این دو API وجود ندارد.
Benchmark — زمان اجرا
در benchmark زیر، local نمایانگر حالت non-escaping و sink نمایانگر حالت escaping است.
همچنین:
no-args
یعنی error فقط شامل یک string ساده است.
int-arg
یعنی error شامل formatting است.
│ old.txt │ new.txt │
│ sec/op │ sec/op vs base │
Errorf/no-args/local-16 63.76n ± 1% 4.874n ± 0% -92.36%
Errorf/no-args/sink-16 64.25n ± 1% 25.81n ± 0% -59.83%
Errorf/int-arg/local-16 90.86n ± 1% 90.97n ± 1% ~
Errorf/int-arg/sink-16 91.81n ± 1% 91.10n ± 1% -0.76%
نکتهٔ مهم این است که بهینهسازی تقریباً کاملاً روی stringهای بدون formatting متمرکز است. وقتی واقعاً formatting انجام میشود، performance تقریباً بدون تغییر باقی میماند.
تعداد بایتهای تخصیصیافته
│ old.txt │ new.txt │
│ B/op │ B/op vs base │
Errorf/no-args/local-16 19.00 ± 0% 0.00 ± 0% -100.00%
Errorf/no-args/sink-16 19.00 ± 0% 16.00 ± 0% -15.79%
Errorf/int-arg/local-16 24.00 ± 0% 24.00 ± 0% ~
Errorf/int-arg/sink-16 24.00 ± 0% 24.00 ± 0% ~
در حالت non-escaping و بدون formatting، allocation از 19 بایت به صفر رسیده است.
تعداد allocationها
│ old.txt │ new.txt │
│ allocs/op │ allocs/op vs base │
Errorf/no-args/local-16 2.000 ± 0% 0.000 ± 0% -100.00%
Errorf/no-args/sink-16 2.000 ± 0% 1.000 ± 0% -50.00%
Errorf/int-arg/local-16 2.000 ± 0% 2.000 ± 0% ~
Errorf/int-arg/sink-16 2.000 ± 0% 2.000 ± 0% ~
نتیجهٔ عملی
از Go 1.26 به بعد، دیگر لازم نیست صرفاً با هدف کاهش allocation، کدهایی مانند:
fmt.Errorf("connection failed")
را به:
errors.New("connection failed")
تبدیل کنید.
اگر پروژه از fmt.Errorf بهصورت یکپارچه برای ساخت error استفاده میکند، استفاده از آن برای errorهای ساده نیز اکنون هزینهٔ بسیار کمتری دارد.
البته وقتی formatting واقعاً نیاز است، همچنان انتخاب طبیعی fmt.Errorf است:
fmt.Errorf("failed to connect to %s", host)
بهینهسازی Go 1.26 بیشتر یک پیام مهم دربارهٔ سادگی API در کنار performance دارد: در بسیاری از موارد دیگر لازم نیست برای چند nanosecond اختلاف، خوانایی و consistency کد را قربانی کنیم. | 126 |
| 8 | 💻 برای کدنویسی بیشتر از کدام ادیتور استفاده میکنید؟ | 665 |
| 9 | شما بیشتر برای نوشتن، دیباگ و بررسی کد روی کدام AI حساب میکنید؟ | 650 |
| 10 | یکی از مهندسان ارشد AWS در این ویدیوی جدید تجربیات عملی تیمهای آمازون را در استفاده از ایجنتهای کدنویسی بررسی کرده؛ جایی که بهرهوری از ۱۰ تا ۲۰ درصد به ۴٫۵ تا ۱۰ برابر رسیده است.
تفاوت اصلی نه در ابزار، بلکه در پنج عادت کاری تیمهای پیشتاز است:
۱. دست کشیدن از کدنویسی دستی: ایجنتها بیش از ۹۸٪ کد را مینویسند و تمرکز مهندسان روی معماری و تصمیمگیری است.
۲. تغذیه ایجنت بهجای نظارت لحظهای: ارائه تسکهای مشخص و معیارهای سنجش خودکار تا ایجنت بتواند ساعتها مستقل کار و خوداصلاحی کند.
۳. اولویت شفافسازی نیازمندیها (Spec-first): چکشکاری سند طراحی فنی بسیار کمهزینهتر از اصلاح کدهای اشتباه است.
۴. انتقال تستها به محیط محلی (Shift-Left): استفاده از سرویسهای شبیهساز محلی (Mock) برای حلقههای بازخورد سریع به ایجنت.
۵. سرمایهگذاری روی کانتکست و ابزارها: توسعه سرورهای MCP اختصاصی و بهینهسازی مداوم فایلهای راهنما (steering).
ویدیو:
https://youtube.com/watch?v=pqlWNihgdjI
@ | <Mehdi Allahyari/> | 301 |
| 11 | 🎖Fake example.com (http://example.com/)
گواهی TLS پیشفرض httptest.Server از قبل example.com را در فیلد DNSNames خود دارد.
در DNSNames فهرستی از hostnameها یا domain nameهایی است که certificate مجاز به secure کردن آنهاست.
با این حال، در Go 1.25، Server.Client به پاسخهایی که واقعاً از example.com میآیند اعتماد نمیکرد:
// go 1.25
func Test(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello"))
})
srv := httptest.NewTLSServer(handler)
defer srv.Close()
_, err := srv.Client().Get("https://example.com")
if err != nil {
t.Fatal(err)
}
}
در نتیجه test با خطای TLS شکست میخورد:
--- FAIL: Test (0.29s)
main_test.go:19: Get "https://example.com":
tls: failed to verify certificate:
x509: certificate signed by unknown authority
مشکل اینجاست که اگرچه certificate مربوط به test server شامل example.com است، اما example.com واقعی توسط همان test server ارائه نمیشود و certificate آن توسط CA مورداعتماد Server.Client صادر نشده است.
رفتار جدید در Go 1.26
برای حل این مشکل، در Go 1.26، HTTP clientای که توسط httptest.Server.Client برگردانده میشود،
درخواستهای مربوط به example.com و subdomainهای آن را به test server هدایت میکند.
بنابراین میتوانیم مستقیماً در test از example.com استفاده کنیم:
// go 1.26
func Test(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello"))
})
srv := httptest.NewTLSServer(handler)
defer srv.Close()
resp, err := srv.Client().Get("https://example.com")
if err != nil {
t.Fatal(err)
}
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
if string(body) != "hello" {
t.Errorf("Unexpected response body: %s", body)
}
}
در این حالت، درخواست:
https://example.com
بهجای اینکه به سرور واقعی example.com برود، توسط client مربوط به httptest.Server به test server هدایت میشود.
در نتیجه handler تست اجرا شده و پاسخ:
hello
دریافت میشود و test با موفقیت پایان مییابد:
PASS
این تغییر باعث میشود نوشتن integration testهایی که نیاز دارند با یک hostname واقعی مانند example.com کار کنند، سادهتر شود؛ بدون اینکه لازم باشد برای حل مشکل certificate یا DNS routing، setup پیچیدهای در تست ایجاد کنیم. | 245 |
| 12 | 🎖Context-Aware Dialing
package net
تعدادی function سطح بالا برای اتصال به یک address از طریق networkهای مختلف دارد:
DialTCP
DialUDP
DialIP
DialUnix
این APIها قبل از معرفی context.Context در Go طراحی شدهاند؛ بنابراین از cancellation پشتیبانی نمیکنند:
raddr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:12345")
conn, err := net.DialTCP("tcp", nil, raddr)
fmt.Printf("connected, err=%v\n", err)
defer conn.Close()
در کنار اینها، type دیگری به نام net.Dialer وجود دارد که متد عمومی DialContext را ارائه میکند.
این متد از cancellation پشتیبانی میکند و میتواند برای اتصال از طریق هر network شناختهشدهای استفاده شود:
var d net.Dialer
ctx := context.Background()
conn, err := d.DialContext(ctx, "tcp", "127.0.0.1:12345")
fmt.Printf("connected, err=%v\n", err)
defer conn.Close()
اما DialContext نسبت به functionهای اختصاصی network مانند net.DialTCP کمی overhead بیشتری دارد؛
دلیل آن، هزینهٔ اضافی مربوط به address resolution و dispatch کردن بر اساس نوع network است.
بنابراین قبل از Go 1.26 با یک تناقض مواجه بودیم:
Network-specific functions
مثل:
net.DialTCP
net.DialUDP
net.DialIP
net.DialUnix
کارایی بیشتری دارند، اما cancellation را پشتیبانی نمیکنند.
در مقابل، net.Dialer و DialContext امکان cancellation را فراهم میکنند، اما به دلیل abstraction و dispatch اضافه، کمی کمکارآمدتر هستند.
تیم Go در نسخهٔ 1.26 این دو ویژگی را با هم ترکیب کرده است.
ا Dialerهای جدید در Go 1.26
اکنون net.Dialer متدهای context-aware جدیدی برای networkهای مختلف دارد:
DialTCP
DialUDP
DialIP
DialUnix
این APIها کارایی functionهای network-specific قدیمی را با قابلیت cancellation از طریق Context ترکیب میکنند:
var d net.Dialer
ctx := context.Background()
raddr := netip.MustParseAddrPort("127.0.0.1:12345")
conn, err := d.DialTCP(ctx, "tcp", netip.AddrPort{}, raddr)
fmt.Printf("connected, err=%v\n", err)
defer conn.Close()
در اینجا دیگر مجبور نیستید بین performance و cancellation یکی را انتخاب کنید؛ میتوانید از API اختصاصی TCP استفاده کنید و همزمان context.Context را برای کنترل lifecycle عملیات در اختیار داشته باشید.
در عمل، این موضوع برای سرویسهای production که dialing باید به timeout، cancellation یا lifecycle درخواست متصل باشد، اهمیت زیادی دارد. | 144 |
| 13 | 🎖Compare IP Subnets
یک IP address prefix یک IP subnet را مشخص میکند. این prefixها معمولاً با استفاده از CIDR notation نوشته میشوند:
10.0.0.0/16
127.0.0.0/8
169.254.0.0/16
203.0.113.0/24
در Go، یک IP prefix با type netip.Prefix نمایش داده میشود.
در Go 1.26، متد جدید Prefix.Compare امکان مقایسهٔ دو IP prefix را فراهم میکند.
این قابلیت مرتبسازی prefixها را بسیار ساده میکند و دیگر نیازی نیست برای مقایسهٔ آنها منطق اختصاصی بنویسید:
prefixes := []netip.Prefix{
netip.MustParsePrefix("10.1.0.0/16"),
netip.MustParsePrefix("203.0.113.0/24"),
netip.MustParsePrefix("10.0.0.0/16"),
netip.MustParsePrefix("169.254.0.0/16"),
netip.MustParsePrefix("203.0.113.0/8"),
}
slices.SortFunc(prefixes, netip.Prefix.Compare)
for _, p := range prefixes {
fmt.Println(p.String())
}
ترتیب مقایسه چگونه است؟
متد Compare دو prefix را با ترتیب مشخصی مقایسه میکند:
۱. ابتدا بر اساس validity
و prefix نامعتبر قبل از prefix معتبر قرار میگیرد.
۲. سپس بر اساس Address Family
ابتدا IPv4 و سپس IPv6 قرار میگیرد:
10.0.0.0/8 < ::/8
۳. سپس بر اساس Masked IP Address
یعنی آدرس network یا همان Network IP:
10.0.0.0/16 < 10.1.0.0/16
۴. سپس بر اساس Prefix Length
اگر network address یکسان باشد، prefix کوتاهتر قبل از prefix بلندتر قرار میگیرد:
10.0.0.0/8 < 10.0.0.0/16
۵. در نهایت بر اساس Unmasked Address
اگر موارد قبلی یکسان باشند، آدرس اصلی، یعنی آدرسی که هنوز mask نشده است، مقایسه میشود:
10.0.0.0/8 < 10.0.0.1/8
در نتیجه ترتیب کلی Compare به این شکل است:
Validity
↓
Address Family
↓
Masked IP Address
↓
Prefix Length
↓
Unmasked Address
این ordering با ترتیب مورد استفاده در Python netaddr.IPNetwork و همچنین convention استاندارد IANA (Internet Assigned Numbers Authority) مطابقت دارد.
در نتیجه، netip.Prefix.Compare یک comparator استاندارد و قابلاتکا برای مرتبسازی IP prefixها فراهم میکند و نیاز به پیادهسازی دستی منطق مقایسه را از بین میبرد. | 133 |
| 14 | 🎖Signal as Cause
تابع signal.NotifyContext یک context برمیگرداند که هنگام دریافت هرکدام از signalهای مشخصشده، cancel میشود.
در نسخههای قبل از Go 1.26، context لغوشده فقط علت استاندارد context canceled را گزارش میکرد:
// go 1.25
// The context will be canceled on SIGINT signal.
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
// Send SIGINT to self.
p, _ := os.FindProcess(os.Getpid())
_ = p.Signal(syscall.SIGINT)
// Wait for SIGINT.
<-ctx.Done()
fmt.Println("err =", ctx.Err())
fmt.Println("cause =", context.Cause(ctx))
در این حالت، اگر SIGINT دریافت شود، ctx cancel خواهد شد؛ اما:
err = context canceled
cause = context canceled
یعنی اطلاعاتی دربارهٔ اینکه دقیقاً کدام signal باعث cancellation شده در اختیار شما قرار نمیگیرد.
رفتار جدید در Go 1.26
در Go 1.26، علت cancellation دقیقتر شده است.
اکنون context.Cause(ctx) مشخص میکند که کدام signal باعث cancel شدن context شده است:
// go 1.26
// The context will be canceled on SIGINT signal.
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
// Send SIGINT to self.
p, _ := os.FindProcess(os.Getpid())
_ = p.Signal(syscall.SIGINT)
// Wait for SIGINT.
<-ctx.Done()
fmt.Println("err =", ctx.Err())
fmt.Println("cause =", context.Cause(ctx))
بنابراین در چنین سناریویی، ctx.Err() همچنان وضعیت عمومی cancellation را نشان میدهد، اما context.Cause(ctx) اطلاعات دقیقتری دربارهٔ علت cancellation در اختیار قرار میدهد؛ یعنی signal دریافتشده.
این تغییر بهخصوص در برنامههای production که shutdown یا cancellation میتواند دلایل مختلفی داشته باشد، برای observability و debugging مفید است.
یک محدودیت مهم
نوعی که برای cause برگردانده میشود، یعنی signal.signalError، بر پایهٔ string است.
بنابراین این type مقدار واقعی os.Signal را در اختیار شما قرار نمیدهد؛
بلکه فقط نمایش متنی (string representation) آن signal را ارائه میکند.
یعنی نمیتوانید از context.Cause(ctx) انتظار داشته باشید که مستقیماً یک مقدار os.Signal قابل مقایسه در اختیار شما قرار دهد. | 183 |
| 15 | نرخ دلار در بازار آزاد ایران با ثبت رکوردی تازه از ۲۱۰ هزار تومان عبور کرد🤬 | 323 |
| 16 | 🔵 عنوان مقاله
7.5x Faster Encryption with simd and simd/archsimd
🟢 خلاصه مقاله:
در نسخه ۱.۲۷ زبان برنامهنویسی Go، توسعهای خاص و نوآورانه به چشم میخورد که تمرکز آن بر بهرهگیری از بستههای جدید و آزمایشی SIMD است. این فناوری توانسته است روند رمزگذاری اطلاعات را به شدت سریعتر کند، بهطوری که اخیراً تحقیقات نشان داده است سرعت رمزنگاری با الگوریتم ChaCha20 تا ۷.۵ برابر افزایش یافته است. این توسعه نشان میدهد چگونه بهرهگیری از قابلیتهای پیشرفته SIMD بدون نیاز به نوشتن کدهای اسمبلی، میتواند در کاهش زمان پردازش نقش مؤثری ایفا کند.
در این پروژه، تیم توسعهدهنده به جای استفاده از کدهای اسمبلی پیچیده، از بستههای جدید و آزمایشی SIMD در زبان Go بهره برده است تا عملیات رمزنگاری را تسریع کند. این کار بخصوص در بستر_ARM64_ نتیجه بسیار مطلوبی داشته است، جایی که نسخه بدون اسمبلی در زبان Go تقریباً با نسخه نوشته شده به صورت دستی برابری میکند. این موفقیت نشان میدهد که با بهرهگیری هوشمندانه از فناوریهای نوین، میتوان کارایی برنامههای رمزنگاری را در سطح بالایی حفظ کرد و در عین حال کدهای سادهتر و قابل نگهداریتر تولید کرد.
بنابراین، این تحولات نه تنها مهم برای توسعه دهندگان برنامههای امنیتی است، بلکه نویدبخش آیندهای است که در آن رمزگذاری سریعتر و کارآمدتر بدون نیاز به تخصص در کدنویسی سطح پایین، امری ممکن خواهد بود. این پیشرفتها نشان میدهد که فناوریهای نوین مانند SIMD میتوانند در توسعه نرمافزارهای امن و بهینه نقش کلیدی ایفا کنند و مسیر را برای نوآوریهای بیشتر هموار سازند.
#رمزگذاری #SIMD #GoLang #توسعه_پیشرفته
🟣لینک مقاله:
https://kerkour.com/golang-chacha20-encryption-simd-archsimd
➖➖➖➖➖➖➖➖
👑 @gopher_academy | 330 |
| 17 | 🔵 عنوان مقاله
Goldmark 2.0: A Markdown Parser Written in Go
🟢 خلاصه مقاله:
در هفتههای اخیر، نسخه جدید و قابل توجهی از Goldmark 2.0 معرفی شده است؛ یک پارسر مارکداون قدرتمند که با زبان برنامهنویسی گو نوشته شده است. این بروزرسانی، اولین تغییر عمده پس از هفت سال است که برای این پارسر استاندارد و مطابق با معیارهای CommonMark در حوزه زبان برنامهنویسی گو ارائه میشود. در این نسخه، چندین ویژگی جدید و بهبودهای مهم دیده میشود که توسعهدهندگان و کاربران را قادر میسازد تا تجربهی بهتری در کار با مارکداون داشته باشند.
یکی از مهمترین تحولات در این نسخه، پشتیبانی از جنریکها است که سطح جدیدی از انعطافپذیری و کارایی را فراهم میکند. این ویژگی به توسعهدهندگان اجازه میدهد تا کدهای سادهتر و قابلارتقا تری بنویسند، بدون اینکه نگران تعارضهای موجود در ساختارهای داده باشند. همچنین، ساختار پارسر و رندرر حالا مستقلتر از هم عمل میکنند، که این موضوع امکان تنظیمات و شخصیسازیهای دقیقتر در خروجیهای غیر HTML را فراهم کرده است. در نهایت، هر گره در ساختار درختی یا AST دارای اطلاعات موقعیت دقیقی است، که این امر در فرآیندهای تحلیل و پردازش متن نقش بسزایی دارد.
در نتیجه، نسخه ۲.۰ Goldmark، یک انقلاب در نحوهی پردازش و تبدیل مارکداون است، که بهرهوری، انعطافپذیری و سازگاری بیشتری نسبت به نسخههای قبلی ارائه میدهد. این بروزرسانی نشان میدهد که توسعهدهندگان این پروژه به دقت و با تمرکز بر نیازهای جامعه، در تلاش برای بهبود و تطابق ابزارهای خود با استانداردهای روز هستند، تا بتوانند در پروژههای مختلف بهترین تجربه را برای کاربران خود رقم بزنند.
#Goldmark #مارکداون #برنامهنویسی #توسعه
🟣لینک مقاله:
https://github.com/yuin/goldmark
➖➖➖➖➖➖➖➖
👑 @gopher_academy | 277 |
| 18 | پس چه زمانی از کدام استفاده کنیم؟👾 | 234 |
| 19 | این تایپ uintptr اصلاً چیه؟
یکی از کاربردهای اصلی آن تعامل با APIهای low-level و unsafe است.
مثلاً:
package main
import (
"fmt"
"unsafe"
)
func main() {
x := 42
p := unsafe.Pointer(&x)
addr := uintptr(p)
fmt.Printf("address = %#x\n", addr)
}
اینجا addr یک آدرس عددی است.
یعنی:
*x
↓
Pointer
↓
unsafe.Pointer
↓
uintptr
↓
numeric address
آیا اصلا uint و uintptr اندازه یکسانی دارند؟
معمولاً بله، ولی مفهومشان متفاوت است.
طبق مشخصات Go، اندازه uintptr به اندازهای است که بتواند مقدار یک pointer را نگه دارد.
مثلاً روی معماری 64-bit:
uint → 64 bit
uintptr → 64 bit
pointer → 64 bit
ولی این به این معنی نیست که:
uint == uintptr
اینها دو type متفاوت هستند:
var a uint
var b uintptr
a = b // compile error
باید conversion صریح انجام شود:
a = uint(b)
اینم بگم که *uint با uintptr کاملاً متفاوت است
این:
*uint
یعنی:
pointer به یک متغیر از نوع
uint
مثلاً:
x := uint(42)
p := &x
نوع p:
*uint
و:
*p
مقدار:
42
است.
در مقابل:
var u uintptr
یک عدد است، نه pointer.
مقایسه
*uint
│
└── pointer
│
└── points to uint object
│
└── 42
uintptr
│
└── unsigned integer
│
└── may contain a numeric memory address | 205 |
| 20 | 🔵 عنوان مقاله
Go 1.27 Released
🟢 خلاصه مقاله:
در نسخهی جدید، یعنی نسخهی ۱.۲۷، تغییرات و بهبودهای قابلتوجهی صورت گرفته است که انتظار میرود توسعهدهندگان و کاربران زبان برنامهنویسی Go را به شدت راضی کند. در طول دورهی گذشته، تمرکز اصلی بر روی آمادهسازی این نسخه و رفع نواقص بوده است، و اکنون با انتشار رسمی، تمامی ویژگیها و اصلاحات نهایی در اختیار کاربران قرار گرفته است. این نسخه با بسیاری از قابلیتهای جدید و بهینهسازیهای عملکردی همراه است که هدف اصلی آن ارتقاء کارایی و سهولت در توسعه برنامههای نوین است.
نسخهی ۱.۲۷، پس از جلسات فنی و بررسیهای متعدد، به پایان رسیده و اکنون آماده است تا در پروژههای مختلف مورد استفاده قرار گیرد. تیم توسعهدهنده، به رهبری Jesús Espino، تلاش کرده است تا با انتشار این نسخه، پایداری و امنیت زبان Go را هر چه بیشتر تضمین کند و امکانات جدیدی را برای برنامهنویسان فراهم آورد. این بروزرسانی، نشان دهندهی تعهد مداوم جامعهی Go به بهبود و توسعهی زبان است که در نتیجه، مزایای فراوانی برای توسعه دهندگان و شرکتهای فناوری ایجاد میکند.
در مجموع، نسخهی ۱.۲۷ گواهی بر تلاش بیوقفه در جهت پیشرفت فناوری و افزایش بهرهوری است. کسانی که از قبل با این زبان کار میکردند، حالا میتوانند از قابلیتهای جدید بهرهمند شده و پروژههای خود را با اطمینان بیشتری پیش ببرند. این نسخه، به عنوان یک دستاورد مهم در مسیر توسعهی Go، نشانگر تعهد جامعه و تیم توسعه به آیندهای روشن و نوآور است.
#برنامه_نویسی #زبان_گو #نسخه_جدید #توسعه_فناوری
🟣لینک مقاله:
https://go.dev/blog/go1.27
➖➖➖➖➖➖➖➖
👑 @gopher_academy | 183 |
