تملك Go (أو Golang) عدد 25 كلمة مفتاحية: break و case و chan و const و continue و default و defer و else و fallthrough و for و func و go و goto و if و import و interface و map و package و range و return و select و struct و switch و type و var. وهي كلمات محجوزة، لذا لا يمكنك استخدامها أسماءً للمتغيرات أو الدوال أو الأنواع. لم تتغير هذه القائمة منذ Go 1.0 في 2012، ولا تزال Go 1.27 تملك الكلمات الـ 25 نفسها (مواصفات Go). في المقابل تملك Python عدد 35 كلمة مفتاحية، و Java عدد 51، و C++ أكثر من 80. وقِصر القائمة مقصود. فقد صُممت Go لتكون صغيرة بما يكفي لتستوعب اللغة كلها في ذهنك.
الخلاصة
- لدى Go 25 كلمة مفتاحية بالضبط. القائمة موجودة في مواصفات Go ولم تتغير منذ Go 1.0.
- تنقسم إلى أربع مجموعات. التصريحات (
packageوimportوconstوvarوtypeوfunc)، والأنواع المركّبة (structوinterfaceوmapوchan)، وتدفق التحكم (12 كلمة مفتاحية منifإلىreturn)، والتزامن والتنظيف (goوselectوdefer). - هناك كلمة مفتاحية واحدة فقط للحلقات. تغطي
forالحلقات ذات العدّاد وحلقات while والحلقات اللانهائية، ومعrangeتغطي المرور على slices و maps و channels والأعداد الصحيحة ودوال المكرِّرات (iterators). anyوintوstringوerrorوnilوtrueوlenوmakeليست كلمات مفتاحية. إنها معرّفات معرّفة مسبقًا (predeclared identifiers)، لذا يمكنك حجبها (shadowing)، مع أنك في العادة لا ينبغي أن تفعل ذلك.- لم تُضف generics أي كلمة مفتاحية. أضافت Go 1.18 معاملات الأنواع (type parameters) بأسماء معرّفة مسبقًا (
anyوcomparable) بدلًا من كلمات محجوزة جديدة.
القائمة الكاملة للكلمات المفتاحية في Go
| الكلمة المفتاحية | الفئة | ما تفعله |
|---|---|---|
package | تصريح | تحدد الحزمة التي ينتمي إليها الملف |
import | تصريح | تستورد حزمًا أخرى |
const | تصريح | تصرّح عن ثابت يُحدَّد وقت الترجمة |
var | تصريح | تصرّح عن متغير |
type | تصريح | تصرّح عن نوع مسمّى أو اسم بديل (alias) |
func | تصريح | تصرّح عن دالة أو method أو دالة حرفية (function literal) |
struct | نوع مركّب | تجمع حقولًا مسمّاة في نوع واحد |
interface | نوع مركّب | تصرّح عن مجموعة methods (أو قيد نوع) |
map | نوع مركّب | تصرّح عن نوع جدول تجزئة (hash map) |
chan | نوع مركّب | تصرّح عن نوع channel |
if | تدفق التحكم | تنفّذ كتلة عندما يكون الشرط صحيحًا |
else | تدفق التحكم | تنفّذ كتلة عندما يكون شرط if خاطئًا |
for | تدفق التحكم | الحلقة الوحيدة في Go |
range | تدفق التحكم | تمرّ على slices و maps والسلاسل النصية و channels والأعداد الصحيحة والمكرِّرات |
switch | تدفق التحكم | تختار فرعًا واحدًا من عدة فروع |
case | تدفق التحكم | فرع واحد من switch أو select |
default | تدفق التحكم | الفرع الذي يُنفَّذ عندما لا تتطابق أي case |
fallthrough | تدفق التحكم | تتابع التنفيذ في case التالية من switch |
break | تدفق التحكم | تخرج من for أو switch أو select |
continue | تدفق التحكم | تنتقل إلى التكرار التالي من الحلقة |
goto | تدفق التحكم | تقفز إلى تسمية (label) داخل الدالة نفسها |
return | تدفق التحكم | تخرج من الدالة، مع قيم أو بدونها |
go | التزامن | تشغّل دالة في goroutine جديدة |
select | التزامن | تنتظر عدة عمليات على channels |
defer | التنظيف | تنفّذ استدعاءً عند عودة الدالة |
ما كلمات التصريح المفتاحية في Go؟
لدى Go ست كلمات مفتاحية للتصريح، ومُحمِّل الإعدادات الصغير هذا يستخدمها كلها:
example.gogopackage main import ( "fmt" "time" ) const defaultTimeout = 5 * time.Second var maxRetries = 3 type Config struct { Addr string Timeout time.Duration } func newConfig(addr string) Config { return Config{Addr: addr, Timeout: defaultTimeout} } func main() { cfg := newConfig(":8080") fmt.Println(cfg.Addr, cfg.Timeout, maxRetries) }
package
يبدأ كل ملف Go بعبارة package. وتشترك جميع الملفات في المجلد الواحد في اسم الحزمة نفسه. والحزمة المسمّاة main لها وضع خاص: فهي تُبنى إلى ملف تنفيذي، ودالة main فيها هي نقطة بداية البرنامج. أما أي اسم آخر فيُبنى إلى مكتبة تستوردها حزم أخرى. ويشرح مقال الكلمة المفتاحية package في Go قواعد التسمية والظهور وحزم _test بالتفصيل.
import
تجعل import الأسماء المُصدَّرة من حزمة أخرى متاحة في الملف، مثل fmt.Println أو http.ListenAndServe. يمكنك تجميع الاستيرادات بين قوسين، أو إعادة تسمية أحدها (yaml "gopkg.in/yaml.v3")، أو استيراد حزمة من أجل آثارها الجانبية فقط باستخدام _. وترفض Go ترجمة ملف فيه استيراد غير مستخدم، لذلك لا يذكر أي ملف حزمة لا يستخدمها. ويشرح مقال الكلمة المفتاحية import في Go الأسماء المستعارة والاستيراد الفارغ واستيراد النقطة ودورات الاستيراد بالتفصيل.
const
تصرّح const عن قيمة ثابتة تُحدَّد وقت الترجمة، مثل مهلة زمنية أو اسم ترويسة. ويمكن أن تكون الثوابت بلا نوع. فالثابت const maxBodyBytes = 1 << 20 ليس له نوع حتى تستخدمه، لذا يعمل بوصفه int أو int64 دون تحويل. وداخل كتلة const يعدّ المعرّف المعرّف مسبقًا iota تصاعديًا بدءًا من 0، وهكذا تكتب Go قيم التعداد (enums). ويشرح مقال الكلمة المفتاحية const في Go الثوابت ذات النوع والثوابت بلا نوع وiota وقيم التعداد بالتفصيل.
var
تصرّح var عن متغير بنوع صريح أو بقيمة ابتدائية أو بكليهما. ويبدأ كل متغير بالقيمة الصفرية لنوعه: 0 أو "" أو false أو nil. وداخل الدوال يستخدم معظم كود Go الصيغة المختصرة name := value بدلًا منها. لكن var تبقى الخيار الصحيح على مستوى الحزمة، وعندما تريد القيمة الصفرية عن قصد، كما في var buf bytes.Buffer. ويشرح مقال الكلمة المفتاحية var في Go القيم الصفرية و:= والتظليل (shadowing) والمتغيرات على مستوى الحزمة بالتفصيل.
type
تصرّح type عن نوع مسمّى جديد، مثل struct أو interface أو نسخة مسمّاة من نوع موجود مثل type UserID int64. ويمكن أن تكون للنوع الجديد methods خاصة به. ومع = تصرّح بدلًا من ذلك عن اسم بديل (type Handler = http.Handler)، وهو مجرد اسم ثانٍ للنوع نفسه. ويشرح مقال الكلمة المفتاحية type في Go الأنواع المُعرَّفة والأسماء البديلة والـ methods والأنواع العامة (generics) بالتفصيل.
func
تصرّح func عن الدوال والـ methods، وتُنشئ أيضًا الدوال الحرفية (closures). ويكون للـ method مُستقبِل (receiver) قبل اسمها: func (s *Server) Start() error. ويمكن لدوال Go أن تعيد عدة قيم، وبهذه الطريقة يُبلَّغ عن الأخطاء: func parse(body []byte) (Request, error). ويشرح مقال الكلمة المفتاحية func في Go الـ methods والـ closures والدوال المتغيرة المعاملات (variadic) والـ functional options بالتفصيل.
ما الكلمات المفتاحية في Go التي تبني الأنواع المركّبة؟
أربع كلمات مفتاحية تبني أنواعًا من أنواع أخرى. والمصفوفات و slices والمؤشرات أنواع مركّبة أيضًا، لكنها تستخدم رموزًا ([N]T و []T و *T) بدلًا من الكلمات المفتاحية.
example.gogotype Notifier interface { Notify(ctx context.Context, msg string) error } type SlackNotifier struct { Channel string } func (s SlackNotifier) Notify(ctx context.Context, msg string) error { fmt.Printf("#%s: %s\n", s.Channel, msg) return nil } func main() { var n Notifier = SlackNotifier{Channel: "alerts"} statusCounts := map[int]int{200: 41, 500: 2} events := make(chan string, 10) events <- fmt.Sprintf("%d server errors", statusCounts[500]) n.Notify(context.Background(), <-events) }
struct
تجمع struct حقولًا مسمّاة في نوع واحد. وهي بديل Go عن الأصناف (classes): تُلحق methods بنوع struct، وتعيد استخدام السلوك عبر تضمين struct داخل أخرى بدلًا من الوراثة منها. وتخبر وسوم struct مثل json:"email" حزمًا مثل encoding/json بكيفية ترميز كل حقل. ويشرح مقال الكلمة المفتاحية struct في Go وسوم struct والـ receivers بالقيمة وبالمؤشر والتضمين ومقارنة الـ structs ونسخها بالتفصيل.
interface
تصرّح interface عن مجموعة من الـ methods. ويحقّق النوع الـ interface بمجرد امتلاكه تلك الـ methods، دون أي تصريح implements، لذا فإن SlackNotifier في المثال أعلاه هو Notifier دون أن يصرّح بذلك. ومنذ Go 1.18 تصف الـ interfaces أيضًا قيود الأنواع في generics، مثل interface{ ~int | ~float64 }. ويشرح مقال الكلمة المفتاحية interface في Go التحقيق الضمني للـ interfaces وقيم الـ interface الفارغة (nil) وتأكيدات النوع (type assertions) والقيود بالتفصيل.
map
تصرّح map عن نوع جدول تجزئة، ويُكتب map[KeyType]ValueType. تُنشئه بقيمة حرفية أو باستخدام make، والـ map التي قيمتها nil تسبب panic عند الكتابة فيها. وقراءة مفتاح غير موجود تعيد القيمة الصفرية، والصيغة ذات القيمتين count, ok := statusCounts[404] تخبرك إن كان المفتاح موجودًا. ولا تحدد المواصفات ترتيب المرور، وبيئة التشغيل تجعله عشوائيًا عن قصد. ويشرح مقال var مقابل make في Go لماذا تسبب الـ map التي قيمتها nil حالة panic عند الكتابة بينما لا تسببها الـ slice التي قيمتها nil. ويشرح مقال الكلمة المفتاحية map في Go صيغة comma-ok وترتيب المرور والمجموعات (sets) والوصول المتزامن وحزمة maps بالتفصيل.
chan
تصرّح chan عن نوع channel، وهو الأنبوب الذي تستخدمه goroutines لتمرير القيم فيما بينها. النوع chan<- Job للإرسال فقط، و <-chan Job للاستقبال فقط، وهذا يتيح لتوقيع الدالة أن يبيّن الاتجاه الذي تستخدمه. وتُنشأ channels باستخدام make، والـ channel ذات المخزن المؤقت (make(chan string, 10)) تحتفظ بالقيم حتى يستقبلها أحد. ويشرح مقال الكلمة المفتاحية chan في Go الـ channels ذات المخزن المؤقت ومن دونه، وإغلاقها، وحالات الـ deadlock، وأنماط select بالتفصيل.
ما الكلمات المفتاحية في Go التي تتحكم في تدفق البرنامج؟
تتولى اثنتا عشرة كلمة مفتاحية التفرّع والحلقات. ولا توجد في Go الكلمات while و do و try و catch ولا المعامل الثلاثي (ternary). فالأخطاء قيم مُعادة عادية تتحقق منها باستخدام if.
example.gogofunc parse(body []byte) (Request, error) { var req Request if err := json.Unmarshal(body, &req); err != nil { return Request{}, fmt.Errorf("decode request: %w", err) } if req.Email == "" { return Request{}, errors.New("email is required") } return req, nil } func countErrors(lines []string) int { errs := 0 for _, line := range lines { if strings.HasPrefix(line, "#") { continue } if line == "EOF" { break } if strings.Contains(line, "level=error") { errs++ } } return errs }
if
تنفّذ if كتلة عندما يكون الشرط صحيحًا. ولا يحتاج الشرط إلى أقواس، لكن الأقواس المعقوفة إلزامية. ويمكن أن تبدأ if بعبارة قصيرة، والمتغير الذي تصرّح عنه لا يوجد إلا داخل if وفروع else التابعة لها. ويستخدم كود Go هذه الصيغة باستمرار، كما في if err := json.Unmarshal(body, &req); err != nil. ويشرح مقال الكلمة المفتاحية if في Go الشروط المقتصرة على النوع bool وعبارات التهيئة والتظليل (shadowing) والعودة المبكرة وغياب العامل الثلاثي بالتفصيل.
else
تُنفَّذ else عندما يكون شرط if خاطئًا، وتربط else if شروطًا إضافية. وتستخدم Go الاصطلاحية else أقل من معظم اللغات. فعندما ينتهي فرع if بـ return، يكون باقي الدالة هو حالة else أصلًا، لذا يُبقي كود Go المسار الطبيعي (happy path) دون إزاحة ويعود مبكرًا عند الأخطاء. ويشرح مقال الكلمة المفتاحية else في Go سلاسل else if ونطاق المتغيرات المعرّفة في if والعودة المبكرة (early return) وغياب العامل الثلاثي بالتفصيل.
for
for هي الكلمة المفتاحية الوحيدة للحلقات في Go. فالصيغة for i := 0; i < n; i++ هي الحلقة التقليدية ذات العدّاد، و for queue.Len() > 0 حلقة while، و for { ... } تتكرر إلى الأبد حتى يخرج منها شيء ما. ومنذ Go 1.22، في الوحدات (modules) التي يصرّح ملف go.mod فيها عن go 1.22 أو أحدث، يحصل كل تكرار على نسخته الخاصة من متغير الحلقة، لذا لم تعد الـ closures و goroutines التي تبدأ داخل حلقة تتشارك متغيرًا واحدًا. ويشرح مقال الكلمة المفتاحية for في Go صيغ الحلقة الثلاث و range ومتغيرات الحلقة في Go 1.22 والتسميات (labels) والأخطاء الشائعة في الحلقات بالتفصيل.
range
تجعل range حلقة for تمرّ على مجموعة. فهي تعطيك الفهرس والقيمة في slices والمصفوفات، والمفتاح والقيمة في maps، وإزاحة البايت و rune في السلاسل النصية، والقيم المستقبَلة في channels. وأضافت Go 1.22 المرور على عدد صحيح (for attempt := range 3 تُنفَّذ بالقيم 0 و 1 و 2)، وأضافت Go 1.23 المرور على دوال المكرِّرات مثل الدالة التي تعيدها maps.Keys(m).
switch
تختار switch فرعًا واحدًا من عدة فروع. وعلى عكس C و Java، تتوقف switch في Go بعد الحالة المطابقة، لذا لا تحتاج أبدًا إلى break في نهاية كل حالة. و switch بلا تعبير تعمل كسلسلة if/else if أنظف، و type switch (switch v := x.(type)) يتفرّع بحسب النوع الديناميكي لقيمة interface.
case
تحدد case فرعًا واحدًا من switch أو select. ويمكن لحالة واحدة أن تسرد عدة قيم: case "GET", "HEAD":. وفي switch تُفحص الحالات من الأعلى إلى الأسفل، وأول تطابق هو الذي يُنفَّذ.
default
default هو الفرع الذي يُنفَّذ عندما لا تتطابق أي case. ويمكن أن يظهر في أي موضع داخل switch، مع أن العُرف يضعه في الآخر. وفي select تجعل حالة default العبارة كلها غير حاجبة (non-blocking): إن لم تكن أي channel جاهزة، يُنفَّذ default فورًا.
fallthrough
تجعل fallthrough عبارة switch تتابع إلى جسم الحالة التالية دون فحص شرط تلك الحالة. وهي نادرة في الكود الحقيقي، وهي الموضع الوحيد الذي تتصرف فيه switch في Go مثل C. والصلاحيات المتدرجة حالة تكون فيها مقروءة بوضوح:
example.gogofunc permissions(role string) []string { var perms []string switch role { case "admin": perms = append(perms, "delete") fallthrough case "editor": perms = append(perms, "write") fallthrough case "viewer": perms = append(perms, "read") default: return nil } return perms }
تعيد permissions("editor") القيمة [write read]، وتعيد permissions("admin") القيمة [delete write read]. ويجب أن تكون fallthrough آخر عبارة في الحالة، وهي غير مسموح بها في type switch.
break
تخرج break من أعمق for أو switch أو select. والجزء الأخير فخ شائع: فـ break داخل select تغادر select فقط، لا الحلقة المحيطة بها. ولمغادرة الحلقة، ضع عليها تسمية واكتب break loop:
example.gogofunc drain(ctx context.Context, events <-chan string) { loop: for { select { case ev, ok := <-events: if !ok { break loop } fmt.Println("event:", ev) case <-ctx.Done(): break loop } } fmt.Println("drained") }
continue
تتخطى continue بقية التكرار الحالي وتبدأ التكرار التالي. وفي countErrors أعلاه تتخطى أسطر التعليقات. ومثل break، يمكن أن تأخذ تسمية لمتابعة حلقة خارجية من داخل حلقة متداخلة.
goto
تقفز goto إلى تسمية داخل الدالة نفسها. ولا يمكنها القفز إلى داخل كتلة أو فوق تصريح متغير، وهذا يستبعد معظم المشكلات التي تسببها goto في C. ونادرًا ما ستكتبها. فهي تظهر أساسًا في الكود المولَّد وفي بعض الأجزاء منخفضة المستوى من المكتبة القياسية. تحتوي الواجهة الخلفية لـ LevelUpGo نفسها على نحو 30,000 سطر من Go خارج الاختبارات وبيانات التهيئة (seed data). ولا تستخدم goto ولا fallthrough ولو مرة واحدة، مقارنةً بأكثر من 250 حلقة range وأكثر من 40 استدعاء defer.
return
تنهي return الدالة وتعيد نتائجها. وغالبًا ما تعيد دوال Go قيمتين، نتيجة و error، ويتحقق المستدعي من الخطأ فورًا. ومع معاملات النتائج المسمّاة، تعيد return المجردة قيمها الحالية، لكن معظم أدلة الأسلوب تقصر ذلك على الدوال القصيرة.
ما الكلمات المفتاحية في Go الخاصة بالتزامن والتنظيف؟
آخر ثلاث كلمات مفتاحية هي go و select و defer. ومع chan تمثّل ما يستخدمه خادم Go لتشغيل الأعمال بالتوازي والتنظيف بعدها.
example.gogofunc startWorkers(ctx context.Context, n int, jobs <-chan string, results chan<- []byte) { for range n { go worker(ctx, jobs, results) } } func worker(ctx context.Context, jobs <-chan string, results chan<- []byte) { for { select { case url, ok := <-jobs: if !ok { return } body, err := fetch(ctx, url) if err != nil { log.Printf("fetch %s: %v", url, err) continue } results <- body case <-ctx.Done(): return } } } func fetch(ctx context.Context, url string) ([]byte, error) { ctx, cancel := context.WithTimeout(ctx, 2*time.Second) defer cancel() req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return nil, err } resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() return io.ReadAll(resp.Body) }
go
تبدأ go استدعاء دالة في goroutine جديدة وتمضي دون انتظاره. وتشغّل startWorkers أعلاه go worker(ctx, jobs, results) داخل حلقة، فتحصل على مجموعة عمّال (worker pool). وتبدأ goroutines بمكدّس (stack) حجمه بضعة كيلوبايتات، لذا يمكن للخادم تشغيل مئات الآلاف منها. وتتجاهل عبارة go القيم التي تعيدها الدالة، لذا تعود النتائج عبر channels، وتنتظر sync.WaitGroup أو errgroup انتهاء العمل.
select
تنتظر select عدة عمليات على channels وتنفّذ أول عملية تصبح جاهزة. وإن كانت عدة عمليات جاهزة في الوقت نفسه، تختار إحداها عشوائيًا. ويستخدمها العامل (worker) أعلاه لأخذ المهمة التالية أو التوقف عند إلغاء الـ context. واقتران channel مع ctx.Done() أو time.After هو الطريقة التي يضيف بها كود Go المهل الزمنية والإلغاء إلى أي عملية حاجبة.
defer
تُجدول defer استدعاءً ليُنفَّذ عند عودة الدالة المحيطة، سواء عادت بشكل طبيعي أو حدثت فيها panic. وهي تُبقي كود التنظيف بجوار الكود الذي يحتاجه: defer resp.Body.Close() و defer mu.Unlock() و defer cancel(). وتُنفَّذ الاستدعاءات المؤجلة بترتيب آخر من يدخل أول من يخرج (LIFO)، وتُقيَّم وسائطها عند تنفيذ سطر defer، لا في النهاية. و defer داخل حلقة لا تُنفَّذ إلا عند عودة الدالة كلها، وهذا من أخطاء Go الشائعة التي تستحق أن تعرفها مبكرًا.
ما الذي لا يُعدّ كلمة مفتاحية في Go؟
كثير من الأسماء التي تبدو مدمجة في اللغة هي معرّفات معرّفة مسبقًا، لا كلمات مفتاحية. وتسردها مواصفات Go في الكتلة الكونية (universe block) التي تحيط بكل حزمة (مواصفات Go):
| النوع | المعرّفات المعرّفة مسبقًا |
|---|---|
| الأنواع | any, bool, byte, comparable, complex64, complex128, error, float32, float64, int, int8, int16, int32, int64, rune, string, uint, uint8, uint16, uint32, uint64, uintptr |
| الثوابت | true, false, iota |
| القيمة الصفرية | nil |
| الدوال | append, cap, clear, close, complex, copy, delete, imag, len, make, max, min, new, panic, print, println, real, recover |
يهمّ هذا الفرق لأن المعرّفات المعرّفة مسبقًا يمكن إعادة التصريح عنها في نطاق داخلي. هذا الكود يُترجم بنجاح:
example.gogofunc logAddrChange(cur, next Config) { old, new := cur.Addr, next.Addr if old != new { log.Printf("addr changed from %s to %s", old, new) } } func countLines(lines []string) int { len := len(lines) return len }
تسمية متغير new شائعة في الكود الذي يقارن القيم القديمة بالجديدة، وهي تعمل. لكن بعد len := len(lines) يفشل أي استدعاء لاحق لـ len(...) في تلك الدالة بالخطأ invalid operation: cannot call len (variable of type int): int is not a function. أما الكلمة المفتاحية فلا يمكن استخدامها بهذه الطريقة إطلاقًا. فالسطر var := 3 يفشل بالخطأ syntax error: unexpected :=, expected name. وتنبّه أدوات الفحص (linters) مثل predeclared في golangci-lint إلى الأسماء المدمجة المحجوبة، لأن كودًا مثل countLines يُترجم لكنه يربك القارئ التالي.
وإبقاء هذه الأسماء خارج قائمة الكلمات المفتاحية يتيح لـ Go أن تنمو دون كسر البرامج الموجودة. فقد أُضيفت any (Go 1.18) و min و max و clear (Go 1.21) بوصفها معرّفات معرّفة مسبقًا، لذا استمر أي كود كان يستخدم هذه الأسماء متغيرات في الترجمة بنجاح.
أين يأتي دور LevelUpGo
يعلّم LevelUpGo لغة Go عبر تمارين تشغّل كود Go حقيقيًا في المتصفح. تغطي دورة Go Basics كلمات التصريح وتدفق التحكم المفتاحية، من var و const إلى switch و for. وتغطي Concurrency Fundamentals الكلمات go و chan و select و defer، وتبيّن Simplification متى تكون switch أوضح من سلسلة if. ويضم Training Ground تمارين قصيرة مستقلة للتدرّب عليها خارج الدورات.
الأسئلة الشائعة
كم عدد الكلمات المفتاحية في Go؟
لدى Go 25 كلمة مفتاحية: break و case و chan و const و continue و default و defer و else و fallthrough و for و func و go و goto و if و import و interface و map و package و range و return و select و struct و switch و type و var. والقائمة معرّفة في مواصفات Go ولم تتغير منذ Go 1.0.
هل any كلمة مفتاحية في Go؟
لا. any معرّف معرّف مسبقًا، وهو اسم بديل لـ interface{} أُضيف في Go 1.18. والأمر نفسه ينطبق على int و string و error و nil و true و false و iota والدوال المدمجة مثل len و make و append. ويمكنك إعادة التصريح عنها في نطاق داخلي، مع أن أدوات الفحص تحذّر عندما تفعل ذلك.
هل يمكنني استخدام كلمة مفتاحية في Go اسمًا لمتغير؟
لا. الكلمات المفتاحية محجوزة، لذا فإن var := 3 أو func type() خطأ في الصياغة (syntax error). والحل المعتاد تنويع قصير على الاسم: كثيرًا ما يستخدم كود Go الاسم typ أو kind بدلًا من type، و pkg بدلًا من package، و fn بدلًا من func.
هل أضافت Go كلمات مفتاحية منذ Go 1.0؟
لا. الكلمات المفتاحية الـ 25 في Go 1.27 هي نفسها الـ 25 التي صدرت مع Go 1.0 في مارس 2012. فقد وصلت generics والمرور بـ range على الأعداد الصحيحة ودوال المكرِّرات والـ methods العامة (generic methods) في Go 1.27 كلها دون كلمات مفتاحية جديدة. ويجعل وعد التوافق في Go 1 إضافة كلمات مفتاحية جديدة أمرًا مستبعدًا، لأن أي كلمة محجوزة جديدة ستكسر كل برنامج يستخدمها اسمًا.
لماذا تملك Go هذا العدد القليل من الكلمات المفتاحية؟
أراد المصممون ذلك. تقول الأسئلة الشائعة عن Go إنهم «حاولوا تقليل الفوضى والتعقيد» وإن «صياغة Go نظيفة وقليلة الكلمات المفتاحية». ولهذا تملك Go كلمة مفتاحية واحدة للحلقات بدلًا من ثلاث، ولهذا تكون الأخطاء قيمًا مُعادة بدلًا من try/catch. والنتيجة لغة يمكنك تعلّمها بسرعة، وكود Go في قواعد الكود المختلفة يُقرأ بطريقة متشابهة إلى حد كبير.
المصادر
- The Go Programming Language Specification, Keywords: https://go.dev/ref/spec#Keywords
- The Go Programming Language Specification, Predeclared identifiers: https://go.dev/ref/spec#Predeclared_identifiers
- Go 1 and the Future of Go Programs (compatibility promise): https://go.dev/doc/go1compat
- Go FAQ, "What are the guiding principles in the design?": https://go.dev/doc/faq#principles
- Go 1.22 release notes (loop variables, range over integers): https://go.dev/doc/go1.22
- Go 1.23 release notes (range over iterator functions): https://go.dev/doc/go1.23
- Python language reference, Keywords: https://docs.python.org/3/reference/lexical_analysis.html#keywords
- Java Language Specification (Java SE 21), Keywords: https://docs.oracle.com/javase/specs/jls/se21/html/jls-3.html#jls-3.9
- cppreference, C++ keywords: https://en.cppreference.com/w/cpp/keyword
