العودة إلى المدونة

الكلمة المفتاحية if في Go: الشروط وعبارات التهيئة والإرجاع المبكر

كيف تعمل if في Go: الأقواس الإلزامية، والشروط من النوع bool فقط، وموضع else، وعبارات التهيئة، والتظليل، والإرجاع المبكر، ولماذا لا يوجد عامل ثلاثي.

الكلمة المفتاحية if في Go: الشروط وعبارات التهيئة والإرجاع المبكر

تشغّل الكلمة المفتاحية if كتلة من الكود حين يكون شرط منطقي صحيحًا. ولنسخة Go منها ثلاث قواعد تفاجئ القادمين من C أو JavaScript أو Python. فالشرط لا يُكتب بين أقواس هلالية، والأقواس المعقوفة إلزامية دائمًا، ويجب أن يكون الشرط قيمة bool حقيقية، لأن Go لا تعرف القيم «الشبيهة بالصحيح» أو «الشبيهة بالخطأ» (truthy و falsy). ويمكن أيضًا أن تبدأ if بعبارة قصيرة، ولهذا يظهر if err := f(); err != nil في كل ملف Go تقريبًا (مواصفات Go).

الخلاصة

  • لا توجد أقواس هلالية حول الشرط في if status >= 500 { ... }، والأقواس المعقوفة إلزامية حتى لو كان الجسم سطرًا واحدًا.
  • يجب أن يكون نوع الشرط bool. فالسطر if retries { ... } يفشل بالخطأ non-boolean condition in if statement.
  • يجب أن تأتي else على السطر نفسه الذي فيه القوس المعقوف المغلق }. وإذا وُضعت على سطر مستقل فهذا خطأ نحوي.
  • السطر if v, err := parse(s); err != nil { ... } يصرّح عن متغيرات موجودة داخل if وكل فروع else التابعة لها، ولا وجود لها بعد ذلك.
  • يمكن أن يظلّل := داخل كتلة if متغيرًا خارجيًا. فيُترجم الكود ويستخدم القيمة القديمة دون أي تنبيه.
  • في كود Go الاصطلاحي تُعاد الأخطاء مبكرًا ويبقى مسار النجاح دون إزاحة. وعادةً تُحذف else التي تأتي بعد return.
  • لا يوجد في Go عامل ثلاثي (ternary). استخدم إسنادًا ثم if، أو cmp.Or للقيم الافتراضية، أو min و max لحصر القيم ضمن نطاق.
  • يُقيَّم && و || تقييمًا مختصرًا (short-circuit)، لذا لا يتبع req.User != nil && req.User.IsAdmin أبدًا مؤشرًا قيمته nil.
  • سلسلة if/else if الطويلة تكون عادةً أوضح إذا كُتبت switch بلا تعبير.

كيف تكتب عبارة if في Go؟

اكتب if ثم تعبيرًا منطقيًا ثم كتلة بين أقواس معقوفة. وهذا تحقق من إعادة المحاولة في عميل HTTP:

example.gogo
package main

import "fmt"

func shouldRetry(status, attempt, maxAttempts int) bool {
	if attempt >= maxAttempts {
		return false
	}
	return status == 429 || status >= 500
}

func main() {
	fmt.Println(shouldRetry(503, 1, 3))
	fmt.Println(shouldRetry(503, 3, 3))
	fmt.Println(shouldRetry(404, 1, 3))
}
example.texttext
true
false
false

لا توجد أقواس هلالية حول attempt >= maxAttempts. يمكنك كتابتها وسيُترجم الكود، لكن gofmt يحذفها في المرة التالية التي يُحفظ فيها الملف. أما الأقواس المعقوفة فإلزامية. فالسطر if attempt >= maxAttempts return false لا يُحلَّل نحويًا، ولا يُحلَّل كذلك شرط يليه أمر على السطر التالي:

example.gogo
if status >= 500
	fmt.Println("server error")
example.texttext
./main.go:8:19: syntax error: unexpected newline, expected { after if clause

الأقواس المعقوفة الإلزامية تمنع خطأً شائعًا في C. تضيف سطرًا ثانيًا تحت if بلا أقواس، فيبدو جزءًا من الشرط، لكنه يُنفَّذ في كل مرة. وكان خطأ TLS الشهير لدى Apple عام 2014 المعروف باسم «goto fail» هو هذا الخطأ بعينه. أما في Go فلكل جسم if حدود صريحة، ويزيحها gofmt بالطريقة نفسها في كل قاعدة كود.

هل توجد في Go قيم truthy و falsy؟

لا. يجب أن يكون الشرط تعبيرًا من النوع bool. فلا يُحوَّل int أو سلسلة نصية أو مؤشر أو slice أو خطأ إلى true أو false تلقائيًا:

example.gogo
retries := 3
if retries {
	fmt.Println("retrying")
}
example.texttext
./main.go:7:5: non-boolean condition in if statement

تكتب المقارنة التي تقصدها: retries > 0 أو name != "" أو user != nil أو len(items) > 0 أو err != nil. وهذا يكلّف بضعة أحرف ويجعل التحقق صريحًا. ففي JavaScript يتخطى if (count) الكتلة حين تكون count مساوية لـ 0، وهذا غالبًا خطأ حين يكون الصفر قيمة صالحة. أما في Go فيرى القارئ دائمًا أي شرط يُختبر.

كيف تعمل else و else if في Go؟

تُنفَّذ else حين يكون الشرط خاطئًا، وتختبر else if شرطًا آخر. وهنا يختار مسجّل الطلبات مستوى السجل بناءً على رمز الحالة:

example.gogo
func logRequest(ctx context.Context, logger *slog.Logger, path string, status int) {
	var level slog.Level
	if status >= 500 {
		level = slog.LevelError
	} else if status >= 400 {
		level = slog.LevelWarn
	} else {
		level = slog.LevelInfo
	}
	logger.Log(ctx, level, "request", "path", path, "status", status)
}
example.texttext
level=INFO msg=request path=/api/orders status=200
level=WARN msg=request path=/api/orders/99 status=404
level=ERROR msg=request path=/api/checkout status=502

تُفحص الفروع من الأعلى إلى الأسفل، ويُنفَّذ أول فرع صحيح. فالرمز 502 يطابق status >= 500 أولًا، لذا لا يصل أبدًا إلى اختبار status >= 400. ويشرح مقال الكلمة المفتاحية else في Go سلاسل else و else if بتفصيل أكبر.

لماذا يجب أن تكون else على سطر القوس المغلق نفسه؟

بسبب الإدراج التلقائي للفواصل المنقوطة. فقواعد Go النحوية تستخدم الفواصل المنقوطة لإنهاء العبارات، ويضيف المحلل المعجمي (lexer) فاصلة منقوطة في نهاية كل سطر ينتهي بـ } أو بمعرّف أو بقيمة حرفية أو ببضعة رموز أخرى (مواصفات Go). لذلك فإن } الموجود وحده على سطر يُنهي عبارة if. ثم تبدأ else في السطر التالي عبارة جديدة، ولا توجد عبارة يمكن أن تبدأ بـ else:

example.gogo
if status >= 500 {
	fmt.Println("server error")
}
else {
	fmt.Println("ok")
}
example.texttext
./main.go:10:2: syntax error: unexpected keyword else, expected }

الحل هو كتابة } else { على سطر واحد. والقاعدة نفسها تفسّر لماذا لا يمكن وضع القوس المعقوف الافتتاحي لـ if أو for أو func على سطر مستقل. ولهذا أيضًا لا تختلف مشاريع Go حول أسلوب الأقواس. فالمترجم لا يقبل إلا موضعًا واحدًا للقوس، ويتولى gofmt بقية التنسيق.

ما عبارة if التي تبدأ بعبارة قصيرة؟

يمكن أن تبدأ if بعبارة قصيرة تفصلها عن الشرط فاصلة منقوطة. تُنفَّذ العبارة أولًا، وأي متغيرات تصرّح عنها يكون نطاقها داخل if فقط. وأشهر استخدام لها هو معالجة الأخطاء:

example.gogo
func loadConfig(path string) (Config, error) {
	var cfg Config
	data, err := os.ReadFile(path)
	if err != nil {
		return cfg, fmt.Errorf("read config: %w", err)
	}
	if err := json.Unmarshal(data, &cfg); err != nil {
		return cfg, fmt.Errorf("parse %s: %w", path, err)
	}
	return cfg, nil
}

لا تعيد json.Unmarshal إلا خطأ، لذا يتسع الاستدعاء والتحقق لسطر واحد، ويختفي ذلك المتغير err بعد القوس المغلق. أما os.ReadFile فتعيد بيانات تحتاج إليها لاحقًا، لذا تأخذ سطرًا خاصًا بها. وهذا هو التقسيم المعتاد في كود Go. ضع العبارة داخل if حين لا تحتاج إلى نتائجها إلا للتحقق، وخارجها حين يستخدمها باقي الدالة.

والصيغة نفسها تعمل مع كل تعبير يعيد قيمة وقيمة منطقية، وهو ما تسمّيه Go نمط comma-ok. فالبحث في map يخبرك هل المفتاح موجود:

example.gogo
roles := map[string]string{"u_42": "admin", "u_7": "billing"}

if role, ok := roles["u_42"]; ok {
	fmt.Println("u_42 is", role)
}
if _, ok := roles["u_99"]; !ok {
	fmt.Println("u_99 has no role")
}
example.texttext
u_42 is admin
u_99 has no role

وتأكيد النوع (type assertion) يتحقق من سلوك اختياري دون خطر حدوث panic. فمعالج البث (streaming) لا يفرّغ المخزن المؤقت إلا حين يدعم http.ResponseWriter ذلك:

example.gogo
if f, ok := w.(http.Flusher); ok {
	f.Flush()
}

أضاف Go 1.26 الدالة errors.AsType، وهي نسخة عامة (generic) من errors.As تعيد الخطأ المطابق وقيمة منطقية. لذا تناسب النمط نفسه. فيمكن لخدمة أن تعود إلى القيم الافتراضية حين يكون ملف الإعدادات مفقودًا، وأن تفشل إذا كان الملف تالفًا:

example.gogo
for _, path := range []string{"missing.json", "bad.json"} {
	_, err := loadConfig(path)
	if pathErr, ok := errors.AsType[*fs.PathError](err); ok {
		fmt.Println("no config file at", pathErr.Path, "so using defaults")
	} else if err != nil {
		fmt.Println("fatal:", err)
	}
}
example.texttext
no config file at missing.json so using defaults
fatal: parse bad.json: invalid character '}' looking for beginning of object key string

أما مع errors.As الأقدم فتصرّح عن var pathErr *fs.PathError قبل if وتمرّر &pathErr، فيبقى المتغير موجودًا بعد انتهاء التحقق.

ما نطاق المتغير المُصرَّح عنه في عبارة if؟

يوجد المتغير في الشرط وفي كتلة if وفي كل كتلة else if و else مرتبطة بها. ولا وجود له بعد انتهاء العبارة (مواصفات Go):

example.gogo
raw := "abc"
if n, err := strconv.Atoi(raw); err != nil {
	fmt.Println("bad MAX_CONNS:", err)
} else {
	fmt.Println("max conns", n)
}
fmt.Println(n)

يستطيع فرع else استخدام n، لكن السطر الأخير يفشل بالخطأ undefined: n. وهذا مقصود. فلا يمكن للمتغير أن يتسرّب إلى كود لا ينبغي أن يعتمد عليه، ويعود الاسم متاحًا في if التالية. وبهذا يمكن أن تحتوي دالة Go واحدة على عشر عمليات تحقق من نوع if err := ...; err != nil دون عشرة أسماء مختلفة للأخطاء.

كيف يسبب := داخل if أخطاء التظليل (shadowing)؟

كل كتلة تفتح نطاقًا جديدًا، و := يصرّح دائمًا في النطاق الحالي. فإذا أسندت قيمة إلى متغير خارجي من داخل كتلة if باستخدام :=، تحصل بدلًا من ذلك على متغير جديد بالاسم نفسه:

example.gogo
func requestTimeout() time.Duration {
	timeout := 5 * time.Second
	if raw := os.Getenv("HTTP_TIMEOUT"); raw != "" {
		timeout, err := time.ParseDuration(raw)
		if err != nil {
			fmt.Println("ignoring HTTP_TIMEOUT:", err)
			return 5 * time.Second
		}
		fmt.Println("HTTP_TIMEOUT set to", timeout)
	}
	return timeout
}
example.texttext
HTTP_TIMEOUT set to 30s
using 5s

المتغير err جديد داخل الكتلة، لذا يكون := صالحًا، لكنه يصرّح بصمت عن متغير timeout ثانٍ أيضًا. فتذهب القيمة المحلَّلة إلى المتغير الداخلي وتختفي عند القوس المغلق. يقبل المترجم هذا الكود، ولا تبلّغ عنه فحوص go vet الافتراضية. والحل هو التصريح عن err باستخدام var err error ثم استخدام =، فتُسند القيمة إلى timeout الخارجي. ويشرح مقال الكلمة المفتاحية var في Go التظليل بعمق أكبر، بما في ذلك المحلّل shadow الذي يكتشف هذه الحالة.

لماذا يتجنب كود Go الاصطلاحي else؟

لأن معظم فروع else في Go تأتي بعد if أعادت بالفعل. وتصوغ Effective Go ذلك هكذا: حين لا تنتقل عبارة if إلى العبارة التالية، لأن جسمها ينتهي بـ break أو continue أو goto أو return، تُحذف else غير الضرورية (Effective Go). والنتيجة كود تُعالَج فيه الأخطاء وتُعاد فور ظهورها، ويسير فيه مسار النجاح مستقيمًا على الحافة اليسرى للدالة.

هذا معالج استرداد (refund) مكتوب بـ if متداخلة لكل تحقق:

example.gogo
func handleRefund(w http.ResponseWriter, r *http.Request) {
	user, ok := userFromContext(r.Context())
	if ok {
		if user.CanRefund {
			var req RefundRequest
			if err := json.NewDecoder(r.Body).Decode(&req); err == nil {
				if req.AmountCents > 0 {
					fmt.Fprintf(w, "refund of %d cents queued for %s", req.AmountCents, req.OrderID)
				} else {
					http.Error(w, "amount_cents must be positive", http.StatusBadRequest)
				}
			} else {
				http.Error(w, "invalid JSON body", http.StatusBadRequest)
			}
		} else {
			http.Error(w, "forbidden", http.StatusForbidden)
		}
	} else {
		http.Error(w, "unauthorized", http.StatusUnauthorized)
	}
}

يعمل هذا الكود، لكن عملية الاسترداد الفعلية تقع على عمق خمسة مستويات، وكل رسالة خطأ بعيدة عن الشرط الذي سببها. ولتعرف لماذا يحصل طلب على 401، عليك أن تطابق آخر else مع أول if. أما عكس كل شرط والإرجاع المبكر فيعطيان السلوك نفسه:

example.gogo
func handleRefund(w http.ResponseWriter, r *http.Request) {
	user, ok := userFromContext(r.Context())
	if !ok {
		http.Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}
	if !user.CanRefund {
		http.Error(w, "forbidden", http.StatusForbidden)
		return
	}

	var req RefundRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "invalid JSON body", http.StatusBadRequest)
		return
	}
	if req.AmountCents <= 0 {
		http.Error(w, "amount_cents must be positive", http.StatusBadRequest)
		return
	}

	fmt.Fprintf(w, "refund of %d cents queued for %s", req.AmountCents, req.OrderID)
}

وتشغيل النسختين عبر httptest بالطلبات الخمسة نفسها يعطي استجابات متطابقة:

example.texttext
401 unauthorized
403 forbidden
400 invalid JSON body
400 amount_cents must be positive
200 refund of 4999 cents queued for ord_1001

كل تحقق في النسخة المسطّحة هو عبارة حماية (guard clause)، أي شرط مقترن مباشرةً باستجابته. وإضافة قاعدة جديدة، مثل حد أقصى للاسترداد، تعني إضافة كتلة if واحدة أخرى فوق السطر الأخير بدلًا من تغليف كل شيء بمستوى إضافي.

ماذا تعني الرسالة "drop this else and outdent its block"؟

هي رسالة القاعدة indent-error-flow في revive، وهي أداة الفحص (linter) التي حلّت محل golint وتعمل داخل golangci-lint. وتُطلَق على كتلة if تنتهي بـ return وتليها else:

example.gogo
func parsePort(raw string) (int, error) {
	if port, err := strconv.Atoi(raw); err != nil {
		return 0, fmt.Errorf("invalid port %q: %w", raw, err)
	} else if port < 1 || port > 65535 {
		return 0, errors.New("port out of range")
	} else {
		return port, nil
	}
}
example.texttext
main.go:14:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)

التلميح الموجود بين القوسين يتعلق بنطاق if. فالمتغير port مُصرَّح عنه في عبارة if، لذا فإن حذف else سيضع return port, nil خارج نطاقه. والحل هو نقل port, err := strconv.Atoi(raw) إلى سطر مستقل فوق if، وعندها يصبح كل تحقق عبارة حماية بسيطة تعيد مباشرةً.

وتغطي القاعدة superfluous-else في revive النمط نفسه بعد break و continue و goto و panic و os.Exit، أما القاعدة الاختيارية early-return فتقترح عكس if/else التي ينتهي فرع else فيها بـ return. ولا يعني أي من هذا أن else خطأ. فمثال logRequest أعلاه يستخدمها استخدامًا صحيحًا، لأن كل فرع يسند قيمة ثم يستمر التنفيذ بعد العبارة. ويشرح مقال الكلمة المفتاحية else في Go متى تكون else هي الخيار الصحيح، وكيف تعيد هيكلة الحالات التي لا تكون فيها كذلك.

هل يوجد عامل ثلاثي في Go؟

لا. تجيب الأسئلة الشائعة في Go عن هذا مباشرةً. فقد رأى المصممون أن ?: «يُستخدم كثيرًا لبناء تعابير معقدة يصعب فهمها»، وقرروا أن «اللغة لا تحتاج إلا إلى بنية واحدة للتحكم الشرطي في التدفق» (الأسئلة الشائعة في Go). والبديل الذي تقترحه هو if تسند قيمة إلى متغير. وفي الممارسة تكون أقصر صيغة هي تعيين القيمة الافتراضية أولًا ثم تجاوزها:

example.gogo
timeout := 5 * time.Second
if cfg.Debug {
	timeout = 5 * time.Minute
}

وتوجد اليوم دوال مساعدة لاستخدامين شائعين للعامل الثلاثي. تعيد cmp.Or، التي أُضيفت في Go 1.22، أول وسيط ليس قيمة صفرية، وهذا يغطي معظم حالات «استخدم هذا وإلا فعُد إلى ذاك». وتغطي الدالتان المدمجتان min و max من Go 1.21 حصر القيم ضمن نطاق:

example.gogo
port := cmp.Or(os.Getenv("PORT"), "8080")
fmt.Println("listening on :" + port)

requested := 500
pageSize := max(1, min(requested, 100))
fmt.Println("page size", pageSize)
example.texttext
listening on :8080
page size 100

تقبل cmp.Or أي عدد من الوسائط، لذا يتحقق cmp.Or(flagAddr, os.Getenv("ADDR"), ":8080") من علم سطر الأوامر أولًا، ثم من متغير البيئة، ثم يعود إلى القيمة الافتراضية.

لماذا لا تكتب دالة ثلاثية عامة؟

يمكنك كتابة func ternary[T any](cond bool, a, b T) T، وبعض قواعد الكود تفعل ذلك. لكن فيها فخًا لا يوجد في صيغة if. فـ Go تقيّم كل الوسائط قبل استدعاء الدالة، لذا يُنفَّذ الفرعان كلاهما دائمًا:

example.gogo
var u *User
name := ternary(u != nil, u.Name, "anonymous")
example.texttext
panic: runtime error: invalid memory address or nil pointer dereference

عامل ?: حقيقي كان سيتخطى u.Name حين تكون u مساوية لـ nil. أما الدالة فتقرأ u.Name أولًا وتنهار. والأمر نفسه ينطبق على الاستدعاءات المكلفة، التي تُنفَّذ سواء استُخدمت نتيجتها أم لا. أما if فلا تقيّم إلا الفرع الذي تسلكه.

كيف يعمل التقييم المختصر لـ && و || داخل if؟

لا يقيّم && طرفه الأيمن إلا حين يكون الطرف الأيسر true، ولا يقيّم || طرفه الأيمن إلا حين يكون الطرف الأيسر false (مواصفات Go). لذا قد يحدد ترتيب الشروط ما إذا كان الكود سيسبب panic. فالتحقق من nil يجب أن يسبق الوصول إلى الحقل:

example.gogo
if req.User != nil && req.User.IsAdmin {
	fmt.Println("admin panel")
} else {
	fmt.Println("access denied")
}

حين تكون req.User مساوية لـ nil يطبع هذا الكود access denied. أما إذا بدّلت الشرطين، كما في req.User.IsAdmin && req.User != nil، فالطلب نفسه يسبب panic بالرسالة invalid memory address or nil pointer dereference، لأن الحقل يُقرأ قبل تنفيذ التحقق.

ويعمل || بالطريقة نفسها في الرفض المبكر. هذه الدالة المساعدة تحلّل ترويسة Authorization وتتوقف عند أول شرط يفشل:

example.gogo
func bearerToken(header string) (string, bool) {
	scheme, token, found := strings.Cut(header, " ")
	if !found || !strings.EqualFold(scheme, "Bearer") || token == "" {
		return "", false
	}
	return token, true
}

مع "Bearer eyJhbGciOi" تعيد الرمز و true. ومع "Basic dXNlcjpwYXNz" ومع "Bearer" وحدها تعيد false. ضع التحققات الرخيصة أولًا والبطيئة، مثل البحث في قاعدة البيانات، في النهاية، حتى لا يُنفَّذ الاستدعاء البطيء إلا حين يمكن أن يغيّر النتيجة. وأولوية && أعلى من ||، لذا فإن a || b && c تعني a || (b && c). أضف أقواسًا هلالية حين تجمع بينهما، فـ gofmt يُبقي عليها.

متى تستخدم switch بدلًا من if/else if؟

حين تقارن قيمة واحدة بعدة حالات، أو حين تحتوي السلسلة على أكثر من فرعين أو ثلاثة من else if. فعبارة switch بلا تعبير تختبر كل case بوصفها قيمة منطقية من الأعلى إلى الأسفل، تمامًا مثل سلسلة if/else if:

example.gogo
switch {
case status >= 500:
	level = slog.LevelError
case status >= 400:
	level = slog.LevelWarn
default:
	level = slog.LevelInfo
}

تصطف الشروط في عمود واحد، ولا حاجة إلى break، لأن switch في Go تتوقف بعد أول حالة مطابقة. وتقبل switch أيضًا عبارة قصيرة، كما في switch ext := filepath.Ext(name); ext { ... }، بقواعد النطاق نفسها التي تنطبق على if. احتفظ بـ if لشرط أو شرطين، وخاصةً لتحققات الأخطاء، حيث يتوقع كل قارئ لكود Go أن يرى if err != nil. ويشرح قسم switch في دليل الكلمات المفتاحية في Go عبارات switch على التعابير، و type switch، و fallthrough.

أين يأتي دور LevelUpGo

يعلّم LevelUpGo لغة Go عبر تمارين تشغّل كود Go حقيقيًا في المتصفح. تغطي دورة Go Basics كلًا من if و else والعوامل المنطقية في وقت مبكر، مع تمارين تجعلك تكتب الشروط بنفسك. وفي دورة Simplification دروس عن تسطيح الكود المتداخل بالإرجاع المبكر، وتبسيط المنطق البولياني، واستبدال سلاسل if الطويلة بـ switch. ويقدّم Training Ground تمارين قصيرة مستقلة للتدرّب خارج أي دورة. وللتعرّف على الكلمات المحجوزة الـ 24 الأخرى، راجع الكلمات المفتاحية في Go: شرح جميع الكلمات الـ 25.

الأسئلة الشائعة

هل يوجد عامل ثلاثي في Go؟

لا. لا يوجد في Go العامل ?:، وتذكر الأسئلة الشائعة في Go أن المصممين استبعدوه لأنه كان يُستخدم كثيرًا لبناء تعابير يصعب قراءتها. استخدم متغيرًا بقيمة افتراضية و if تتجاوزها. وللقيم البديلة تعيد cmp.Or(a, b) أول وسيط غير صفري، وتتولى min و max حصر القيم ضمن نطاق.

لماذا تشترط Go الأقواس المعقوفة مع if؟

تجعل الأقواس المعقوفة حدود كل جسم if صريحة، فلا يمكن لإضافة سطر أن تغيّر بالخطأ العبارات التي يتحكم فيها الشرط. وهي تسمح أيضًا لـ Go بالاستغناء عن الأقواس الهلالية حول الشرط، لأن { يحدد موضع انتهاء الشرط. ثم ينسّق gofmt كل if بالطريقة نفسها.

هل توجد في Go قيم truthy و falsy؟

لا. يجب أن يكون شرط if من النوع bool. فالأرقام والسلاسل النصية والمؤشرات والـ slices والأخطاء لا تُحوَّل تلقائيًا أبدًا، والسطر if count { ... } يفشل بالخطأ non-boolean condition in if statement. اكتب المقارنة صراحةً، مثل count > 0 أو s != "" أو err != nil.

هل يمكن لعبارة if أن تصرّح عن متغير في Go؟

نعم. يمكن أن تبدأ if بعبارة قصيرة، كما في if err := save(order); err != nil. والمتغيرات المُصرَّح عنها هناك تكون ضمن النطاق في الشرط وكتلة if وأي كتل else if أو else، وتختفي بعد انتهاء العبارة.

لماذا يجب أن تكون else على سطر القوس المغلق نفسه في Go؟

يُدرج المحلل المعجمي في Go فاصلة منقوطة في نهاية كل سطر ينتهي بـ }. فإذا بدأت else السطر التالي، تكون عبارة if قد انتهت بالفعل، ويبلّغ المترجم عن syntax error: unexpected keyword else, expected }. وكتابة } else { على سطر واحد تتجنب الفاصلة المنقوطة المُدرجة.

المصادر

اكتب Go كما يكتبها مهندس أول

دروس تفاعلية في متصفحك. الدروس الأولى مجانية.

جرّب درسًا مجانيًاأو أنشئ حسابًا مجانيًا