الكتاب الأساسي

تدفّقات التحكم الأساسية

كتل شيفرة تدفّق التحكم في Go تشبه كثيراً لغات البرمجة الشائعة الأخرى، لكن ثمة اختلافات كثيرة أيضاً. سيعرض هذا المقال أوجه الشبه وأوجه الاختلاف هذه.

مقدمة عن تدفّقات التحكم في Go

هناك ثلاثة أنواع من كتل شيفرة تدفّق التحكم الأساسية في Go:

  • if-else كتلة تنفيذ شرطي ثنائية الاتجاه.
  • for كتلة حلقة.
  • switch-case كتلة تنفيذ شرطي متعددة الاتجاهات.

وهناك أيضاً بعض كتل شيفرة تدفّق التحكم المرتبطة بأنواع معينة من الأنواع في Go.

وكما في العديد من اللغات الشائعة الأخرى، تدعم Go أيضاً جمل قفز تنفيذ الشيفرة break وcontinue وgoto. وإلى جانب هذه، توجد جملة قفز خاصة في Go هي fallthrough.

من بين الأنواع الستة من كتل تدفّق التحكم، باستثناء تدفّق التحكم if-else، تُسمى الخمسة الأخرى كتل تدفّق تحكم قابلة للكسر (breakable). يمكننا استخدام جمل break لجعل التنفيذ يقفز خارج كتل تدفّق التحكم القابلة للكسر.

تُسمى كتلتا الحلقة for وfor-range كتل تدفّق تحكم حلقية. يمكننا استخدام جمل continue لإنهاء دورة حلقة مبكراً في كتلة تدفّق تحكم حلقية، أي الانتقال إلى الدورة التالية من الحلقة.

لاحظ أن كل كتلة من كتل تدفّق التحكم المذكورة أعلاه هي جملة، وقد تحتوي على العديد من الجمل الفرعية الأخرى.

جمل تدفّق التحكم المذكورة أعلاه هي كل الجمل بالمعنى الضيق. أما الآليات المقدَّمة في المقال التالي، goroutines واستدعاءات الدوال المؤجلة وpanic/recover، وتقنيات مزامنة التزامن المقدَّمة في المقال اللاحق نظرة عامة على مزامنة التزامن، فيمكن اعتبارها جمل تدفّق تحكم بالمعنى الواسع.

يشرح المقال الحالي بشكل أساسي كتل شيفرة تدفّق التحكم الأساسية وجمل قفز تنفيذ الشيفرة. كما سنتطرق إلى حلقات for range anInteger {...} التي قدّمتها Go 1.22. وستُشرح كتل تدفّق التحكم الأخرى في العديد من مقالات Go 101 القادمة.

كتل تدفّق التحكم if-else

الصيغة الكاملة لكتلة شيفرة if-else هي كالتالي


if InitSimpleStatement; Condition {
	// افعل شيئاً
} else {
	// افعل شيئاً
}

if وelse كلمتان مفتاحيتان. وكما في العديد من لغات البرمجة الأخرى، فرع else اختياري.

جزء InitSimpleStatement اختياري أيضاً. ويجب أن يكون جملة بسيطة إن وُجد. وإذا كان غائباً، يمكننا اعتباره جملة فارغة (أحد أنواع الجمل البسيطة). عملياً، غالباً ما يكون InitSimpleStatement تعريف متغير قصير أو إسناداً محضاً. ويجب أن يكون Condition تعبيراً نتيجته قيمة منطقية. ويمكن إحاطة جزء Condition بزوج من () أو عدم إحاطته، لكن لا يمكن إحاطته مع جزء InitSimpleStatement معاً.

إذا وُجد InitSimpleStatement في كتلة if-else، فسيُنفَّذ قبل تنفيذ الجمل الأخرى في كتلة if-else. وإذا غاب InitSimpleStatement، تكون الفاصلة المنقوطة التي تليه اختيارية.

كل تدفّق تحكم if-else يشكّل كتلة شيفرة ضمنية واحدة، وكتلة شيفرة صريحة واحدة لفرع if، وكتلة شيفرة اختيارية لفرع else. وكتلتا شيفرة الفرعين متداخلتان في الكتلة الضمنية. عند التنفيذ، إذا كانت نتيجة تعبير Condition هي true، فستُنفَّذ كتلة فرع if، وإلا فستُنفَّذ كتلة فرع else.

مثال:


package main

import (
	"fmt"
	"math/rand"
)

func main() {
	if n := rand.Int(); n%2 == 0 {
		fmt.Println(n, "is an even number.")
	} else {
		fmt.Println(n, "is an odd number.")
	}

	n := rand.Int() % 2 // هذا n ليس الـ n أعلاه.
	if n % 2 == 0 {
		fmt.Println("An even number.")
	}

	if ; n % 2 != 0 {
		fmt.Println("An odd number.")
	}
}

إذا كان InitSimpleStatement في كتلة شيفرة if-else تعريفاً متغيراً قصيراً، فستُعتبر المتغيرات المعرَّفة معرَّفة في الكتلة الضمنية العليا المُتداخلة لكتلة شيفرة if-else.

يمكن أن تكون كتلة شيفرة فرع else ضمنية إذا تبعها else آخر لكتلة شيفرة if-else، وإلا فيجب أن تكون صريحة.

مثال:


package main

import (
	"fmt"
	"time"
)

func main() {
	if h := time.Now().Hour(); h < 12 {
		fmt.Println("Now is AM time.")
	} else if h > 19 {
		fmt.Println("Now is evening time.")
	} else {
		fmt.Println("Now is afternoon time.")
		h := h // الذي على اليمين معرَّف أعلاه
		// المتغير "h" المعرَّف حديثاً
		// يحجب المتغير أعلاه الذي يحمل الاسم نفسه.
		_ = h
	}

	// h غير مرئي هنا.
}

كتل تدفّق التحكم لحلقة for

الصيغة الكاملة لكتلة حلقة for هي:


for InitSimpleStatement; Condition; PostSimpleStatement {
	// افعل شيئاً
}

for كلمة مفتاحية. ويجب أن يكون جزءا InitSimpleStatement وPostSimpleStatement جملتين بسيطتين، ولا يجوز أن يكون جزء PostSimpleStatement تعريف متغير قصير. ويجب أن يكون Condition تعبيراً نتيجته قيمة منطقية. والأجزاء الثلاثة كلها اختيارية.

وخلافاً للعديد من لغات البرمجة الأخرى، لا يمكن إحاطة الأجزاء الثلاثة المذكورة للتو التالية لكلمة for المفتاحية بزوج من ().

كل تدفّق تحكم for يشكّل كتلتَي شيفرة على الأقل، إحداهما ضمنية والأخرى صريحة. والكتلة الصريحة متداخلة في الكتلة الضمنية.

سيُنفَّذ InitSimpleStatement في كتلة حلقة for (مرة واحدة فقط) قبل تنفيذ الجمل الأخرى في كتلة حلقة for.

سيُقيَّم تعبير Condition عند كل دورة من دورات الحلقة. وإذا كانت نتيجة التقييم false، فستنتهي الحلقة. وإلا فسيُنفَّذ جسم الحلقة (أي كتلة الشيفرة الصريحة).

سيُنفَّذ PostSimpleStatement عند نهاية كل دورة من دورات الحلقة.

مثال على حلقة for. سيطبع المثال الأعداد الصحيحة من 0 إلى 9.


for i := 0; i < 10; i++ {
	fmt.Println(i)
}

إذا كان جزءا InitSimpleStatement وPostSimpleStatement غائبين معاً (اعتبرهما مجرد جملتين فارغتين)، يمكن حذف الفاصلتين المنقوطتين المجاورتين لهما. وتسمى هذه الصيغة صيغة حلقة for ذات الشرط فقط. وهي مماثلة لحلقة while في اللغات الأخرى.


var i = 0
for ; i < 10; {
	fmt.Println(i)
	i++
}
for i < 20 {
	fmt.Println(i)
	i++
}

إذا غاب جزء Condition، فسيعتبره المصرِّفون true.


for i := 0; ; i++ { // <=> for i := 0; true; i++ {
	if i >= 10 {
		// ستُشرح جملة "break" أدناه.
		break
	}
	fmt.Println(i)
}

// الحلقات اللانهائية الأربع التالية
// مكافئة لبعضها البعض (في معظم الحالات).
for ; true; {
}
for true {
}
for ; ; {
}
for {
}

إذا كان InitSimpleStatement في كتلة for جملة تعريف متغير قصير، فستُعتبر متغيرات الحلقة المعرَّفة معرَّفة في الكتلة الضمنية العليا المُتداخلة لكتلة for. مثلاً، مقطع الشيفرة التالي يطبع 012 بدلاً من 0.


for i := 0; i < 3; i++ {
	fmt.Print(i)
	// المتغير i على اليسار معرَّف جديد،
	// والمتغير i على اليمين هو متغير الحلقة.
	i := i
	// المتغير المعرَّف الجديد يُعدَّل، لكن
	// القديم (متغير الحلقة) لم يُعدَّل بعد.
	i = 10
	_ = i
}

ملاحظة: عدّلت Go 1.22 دلالات كتل حلقة for.

  • قبل Go 1.22، كان كل متغير حلقة معرَّف يُستخدم في كتلة حلقة for مشتركاً بين جميع الدورات أثناء تنفيذ كتلة الحلقة.
  • منذ Go 1.22، سيُجسَّد كل متغير حلقة معرَّف يُستخدم في حلقة for كنسخة مميزة عند بداية كل دورة.

في معظم الحالات، لا يغيّر التغيير الدلالي سلوك الشيفرة. لكن أحياناً يغيّره. لذا يكسر التغيير الدلالي التوافق العكسي. ومنذ Go 1.22، ينبغي تحديد إصدار Go لكل ملف مصدري لتقليل الضرر إلى أدنى حد ممكن.

يمكن استخدام جملة break لجعل التنفيذ يقفز خارج كتلة تدفّق تحكم حلقة for مبكراً، إذا كانت كتلة تدفّق تحكم حلقة for هي الكتلة القابلة للكسر الأكثر داخلية التي تحتوي جملة break. مثلاً، تطبع الشيفرة التالية أيضاً 0 إلى 9.


i := 0
for {
	if i >= 10 {
		break
	}
	fmt.Println(i)
	i++
}

يمكن استخدام جملة continue لإنهاء دورة الحلقة الحالية مبكراً (سيظل PostSimpleStatement يُنفَّذ)، إذا كانت كتلة تدفّق تحكم حلقة for هي الكتلة الحلقية الأكثر داخلية التي تحتوي جملة continue. مثلاً، سيطبع مقطع الشيفرة التالي 13579.


for i := 0; i < 10; i++ {
	if i % 2 == 0 {
		continue
	}
	fmt.Print(i)
}

استخدام كتل تدفّق التحكم for-range للتنقل عبر الأعداد الصحيحة

يمكن استخدام كتل حلقة for-range للتنقل عبر الأعداد الصحيحة، وجميع أنواع الحاويات، والقنوات، وبعض الدوال. يشرح المقال الحالي فقط كيفية استخدام كتل حلقة for-range للتنقل عبر الأعداد الصحيحة.

ملاحظة: استخدام كتل حلقة for-range للتنقل عبر الأعداد الصحيحة مدعوم فقط منذ Go 1.22.

الشيفرة التالية


// i معرَّف سابقاً.
for i = range anInteger {
	...
}

هي في الواقع صيغة مختصرة لـ


for i = 0; i < anInteger; i++ {
	...
}

وبالمثل،


for i := range anInteger {
	...
}

هي في الواقع صيغة مختصرة لـ


for i := 0; i < anInteger; i++ {
	...
}

بدلالات Go 1.22+.

مثلاً، المثال الأخير في القسم الأخير مكافئ لـ


for i := range 10 {
	if i % 2 == 0 {
		continue
	}
	fmt.Print(i)
}

كتل تدفّق التحكم switch-case

كتلة تدفّق التحكم switch-case هي أحد أنواع كتل تدفّق التحكم ذات التنفيذ الشرطي.

الصيغة الكاملة لكتلة switch-case هي


switch InitSimpleStatement; CompareOperand0 {
case CompareOperandList1:
	// افعل شيئاً
case CompareOperandList2:
	// افعل شيئاً
...
case CompareOperandListN:
	// افعل شيئاً
default:
	// افعل شيئاً
}

في الصيغة الكاملة،

  • switch وcase وdefault ثلاث كلمات مفتاحية.
  • يجب أن يكون جزء InitSimpleStatement جملة بسيطة. وجزء CompareOperand0 تعبير يُنظر إليه كقيمة ذات نوع (وإذا كان قيمة بلا نوع، فيُنظر إليه كقيمة نوعية من نوعه الافتراضي)، لذا لا يمكن أن يكون nil بلا نوع. ويسمى CompareOperand0 بتعبير switch في مواصفات Go.
  • يجب أن يكون كل جزء من CompareOperandListX (حيث قد يمثل X من 1 إلى N) قائمة تعبيرات مفصولة بفواصل. ويجب أن تكون كل من هذه التعبيرات قابلة للمقارنة مع CompareOperand0. ويسمى كل من هذه التعبيرات بتعبير case في مواصفات Go. وإذا كان تعبير case قيمة بلا نوع، فيجب أن يكون قابلاً للتحويل ضمنياً إلى نوع تعبير switch في تدفّق التحكم switch-case نفسه. وإذا تعذّر تحقيق التحويل، يفشل التصريف.

كل case CompareOperandListX: أو default: يفتح (ويتبعه) كتلة شيفرة ضمنية. وتشكّل الكتلة الضمنية مع case CompareOperandListX: أو default: فرعاً. ووجود كل فرع من هذه الفروع اختياري. وسنسمي كتلة شيفرة ضمنية في فرع كهذا كتلة شيفرة فرع فيما يلي.

يمكن أن يوجد فرع default واحد على الأكثر في كتلة تدفّق تحكم switch-case.

إلى جانب كتل شيفرة الفروع، يشكّل كل تدفّق تحكم switch-case كتلتَي شيفرة، إحداهما ضمنية والأخرى صريحة. والكتلة الصريحة متداخلة في الكتلة الضمنية. وكل كتل شيفرة الفروع متداخلة في الكتلة الصريحة (ومتداخلة في الكتلة الضمنية بشكل غير مباشر).

كتل تدفّق التحكم switch-case قابلة للكسر، لذا يمكن استخدام جمل break أيضاً في أي كتلة شيفرة فرع في كتلة تدفّق تحكم switch-case لجعل التنفيذ يقفز خارج كتلة تدفّق التحكم switch-case مبكراً.

سيُنفَّذ InitSimpleStatement أولاً عند تنفيذ تدفّق تحكم switch-case. وسيُنفَّذ مرة واحدة فقط أثناء تنفيذ تدفّق التحكم switch-case. وبعد تنفيذ InitSimpleStatement، سيُقيَّم تعبير switch CompareOperand0 ولن يُقيَّم إلا مرة واحدة. ونتيجة التقييم دائماً قيمة ذات نوع. وستُقارن نتيجة التقييم (باستخدام المعامل ==) بنتيجة تقييم كل تعبير case في قوائم تعبيرات CompareOperandListX، من الأعلى إلى الأسفل ومن اليسار إلى اليمين. وإذا وُجد تعبير case مساوياً لـ CompareOperand0، تتوقف عملية المقارنة وستُنفَّذ كتلة شيفرة الفرع المقابلة للتعبير. وإذا لم يُوجد أي تعبير case مساوياً لـ CompareOperand0، فستُنفَّذ كتلة شيفرة الفرع الافتراضي (إن وُجد).

مثال على تدفّق تحكم switch-case:


package main

import (
	"fmt"
	"math/rand"
)

func main() {
	switch n := rand.Intn(100); n%9 {
	case 0:
		fmt.Println(n, "is a multiple of 9.")

		// خلافاً للعديد من اللغات الأخرى،
		// في Go، سيقفز التنفيذ تلقائياً
		// خارج كتلة switch-case عند
		// نهاية كل كتلة فرع.
		// لا حاجة لجملة "break" هنا.
	case 1, 2, 3:
		fmt.Println(n, "mod 9 is 1, 2 or 3.")
		// هنا، جملة "break" هذه بلا معنى.
		break
	case 4, 5, 6:
		fmt.Println(n, "mod 9 is 4, 5 or 6.")
	// case 6, 7, 8:
		// قد يفشل سطر case أعلاه في التصريف،
		// لأن 6 مكرر مع 6 في case
		// الأخير. السلوك يعتمد على المصرِّف.
	default:
		fmt.Println(n, "mod 9 is 7 or 8.")
	}
}

تُرجع الدالة rand.Intn قيمة int عشوائية غير سالبة أصغر من المعطى المحدد.

لاحظ، إذا أمكن كشف تساوي أي تعبيرَي case في تدفّق تحكم switch-case في وقت التصريف، فقد يرفض المصرِّف التعبير الأخير. مثلاً، يعتبر المصرِّف القياسي لـ Go أن سطر case 6, 7, 8 في المثال أعلاه غير صالح إذا لم يكن ذلك السطر معلَّقاً. لكن قد تعتبر مصرِّفات أخرى ذلك السطر صالحاً. في الواقع، المصرِّف القياسي الحالي لـ Go (الإصدار 1.25.n) يسمح بتكرار تعبيرات case المنطقية، وgccgo (v8.2) يسمح بتكرار تعبيرات case المنطقية والنصية معاً.

وكما تصف التعليقات في المثال أعلاه، خلافاً للعديد من اللغات الأخرى، في Go، عند نهاية كل كتلة شيفرة فرع، سيتوقف التنفيذ تلقائياً خارج كتلة تحكم switch-case المقابلة. فكيف نجعل التنفيذ ينساب إلى كتلة شيفرة الفرع التالية؟ توفر Go الكلمة المفتاحية fallthrough للقيام بهذه المهمة. مثلاً، في المثال التالي، ستُنفَّذ كل كتلة شيفرة فرع، بترتيبها، من الأعلى إلى الأسفل.


switch n := rand.Intn(100) % 5; n {
case 0, 1, 2, 3, 4:
	fmt.Println("n =", n)
	// جملة "fallthrough" تجعل
	// التنفيذ ينساب إلى الفرع التالي.
	fallthrough
case 5, 6, 7, 8:
	// متغير معرَّف جديد يسمى "n" أيضاً،
	// وهو مرئي فقط في كتلة شيفرة
	// الفرع الحالية.
	n := 99
	fmt.Println("n =", n) // 99
	fallthrough
default:
	// هذا "n" هو تعبير switch "n".
	fmt.Println("n =", n)
}

لاحظ،

  • يجب أن تكون جملة fallthrough الجملة الأخيرة في فرع.
  • لا يمكن أن تظهر جملة fallthrough في الفرع الأخير في كتلة تدفّق تحكم switch-case.

مثلاً، استخدامات fallthrough التالية كلها غير قانونية.


switch n := rand.Intn(100) % 5; n {
case 0, 1, 2, 3, 4:
	fmt.Println("n =", n)
	// كتلة if، وليست جملة fallthrough،
	// هي الجملة الأخيرة في هذا الفرع.
	if true {
		fallthrough // خطأ: ليست الجملة الأخيرة
	}
case 5, 6, 7, 8:
	n := 99
	fallthrough // خطأ: ليست الجملة الأخيرة
	_ = n
default:
	fmt.Println(n)
	fallthrough // خطأ: تظهر في الفرع الأخير
}

جزءا InitSimpleStatement وCompareOperand0 في تدفّق تحكم switch-case اختياريان معاً. وإذا غاب جزء CompareOperand0، فسيُنظر إليه كـ true، قيمة ذات نوع من النوع المدمج bool. وإذا غاب جزء InitSimpleStatement، يمكن حذف الفاصلة المنقوطة التي تليه.

وكما ذُكر أعلاه، كل الفروع اختيارية. لذا فكتل الشيفرة التالية كلها قانونية، ويمكن اعتبارها كلها عمليات لا تفعل شيئاً.


switch n := 5; n {
}

switch 5 {
}

switch _ = 5; {
}

switch {
}

بالنسبة لكتلتَي تدفّق التحكم switch-case الأخيرتين في المثال الأخير، وكما ذُكر أعلاه، يُنظر إلى كل جزء CompareOperand0 غائب كقيمة ذات نوع true من النوع المدمج bool. لذا سيطبع مقطع الشيفرة التالي hello.


switch { // <=> switch true {
case true: fmt.Println("hello")
default: fmt.Println("bye")
}

هناك اختلاف واضح آخر عن العديد من اللغات الأخرى، وهو أن موضع فرع default في كتلة تدفّق تحكم switch-case يمكن أن يكون اعتباطياً. مثلاً، كتل تدفّق التحكم switch-case الثلاث التالية مكافئة لبعضها البعض.


switch n := rand.Intn(3); n {
case 0: fmt.Println("n == 0")
case 1: fmt.Println("n == 1")
default: fmt.Println("n == 2")
}

switch n := rand.Intn(3); n {
default: fmt.Println("n == 2")
case 0: fmt.Println("n == 0")
case 1: fmt.Println("n == 1")
}

switch n := rand.Intn(3); n {
case 0: fmt.Println("n == 0")
default: fmt.Println("n == 2")
case 1: fmt.Println("n == 1")
}

جملة goto وتعريف اللافتة

كما في العديد من اللغات الأخرى، تدعم Go أيضاً جملة goto. ويجب أن تتبع الكلمة المفتاحية goto لافتة لتشكيل جملة. وتُعرَّف اللافتة بالصيغة LabelName:، حيث يجب أن يكون LabelName معرّفاً. ويجب استخدام كل لافتة لا يكون اسمها المعرّف الفارغ مرة واحدة على الأقل.

ستجعل جملة goto التنفيذ يقفز إلى الجملة التالية لتعريف اللافتة المستخدمة في جملة goto. لذا يجب أن يتبع تعريف اللافتة جملة واحدة.

يجب تعريف اللافتة داخل جسم دالة. ويمكن أن يظهر استخدام اللافتة قبل تعريفها أو بعده. لكن اللافتة غير مرئية (ولا يمكن أن تظهر) خارج الكتلة الأكثر داخلية التي عُرِّفت فيها.

يستخدم المثال التالي جملة goto ولافتة لتنفيذ تدفّق تحكم حلقي.


package main

import "fmt"

func main() {
	i := 0

Next: // هنا، تُعرَّف لافتة.
	fmt.Println(i)
	i++
	if i < 5 {
		goto Next // يقفز التنفيذ
	}
}

كما ذُكر أعلاه، اللافتة غير مرئية (ولا يمكن أن تظهر) خارج الكتلة الأكثر داخلية التي عُرِّفت فيها. لذا يفشل المثال التالي في التصريف.


package main

func main() {
goto Label1 // خطأ
	{
		Label1:
		goto Label2 // خطأ
	}
	{
		Label2:
	}
}

لاحظ أنه إذا عُرِّفت لافتة داخل نطاق متغير، فلا يمكن أن تظهر استخدامات اللافتة قبل تعريف المتغير. وستُشرح نطاقات المعرّفات في مقال الكتل والنطاقات في Go لاحقاً.

يفشل المثال التالي أيضاً في التصريف.


package main

import "fmt"

func main() {
	i := 0
Next:
	if i >= 5 {
		// خطأ: يقفز فوق تعريف k
		goto Exit
	}

	k := i + i
	fmt.Println(k)
	i++
	goto Next

// هذه اللافتة معرَّفة في نطاق k،
// لكن استخدامها خارج نطاق k.
Exit:
}

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

إحدى الطريقتين هي تقليص نطاق المتغير k.


func main() {
	i := 0
Next:
	if i >= 5 {
		goto Exit
	}
	// إنشاء كتلة شيفرة صريحة
	// لتقليص نطاق k.
	{
		k := i + i
		fmt.Println(k)
	}
	i++
	goto Next
Exit:
}

والطريقة الأخرى هي توسيع نطاق المتغير k.


func main() {
	var k int // انقل تعريف k إلى هنا.
	i := 0
Next:
	if i >= 5 {
		goto Exit
	}

	k = i + i
	fmt.Println(k)
	i++
	goto Next
Exit:
}

جملتا break وcontinue مع اللافتات

يجب أن تحتوي جملة goto على لافتة. ويمكن أن تحتوي جملة break أو continue أيضاً على لافتة، لكن اللافتة اختيارية. وعموماً، تُستخدم جمل break التي تحتوي لافتات في كتل تدفّق التحكم المتداخلة القابلة للكسر، وتُستخدم جمل continue التي تحتوي لافتات في كتل تدفّق التحكم الحلقية المتداخلة.

إذا احتوت جملة break على لافتة، فيجب أن تُعرَّف اللافتة قبل كتلة تدفّق تحكم قابلة للكسر تحتوي جملة break مباشرةً. ويمكننا اعتبار اسم اللافتة اسم كتلة تدفّق التحكم القابلة للكسر. وستجعل جملة break التنفيذ يقفز خارج كتلة تدفّق التحكم القابلة للكسر، حتى لو لم تكن كتلة تدفّق التحكم القابلة للكسر هي الكتلة الأكثر داخلية التي تحتوي جملة break.

إذا احتوت جملة continue على لافتة، فيجب أن تُعرَّف اللافتة قبل كتلة تدفّق تحكم حلقية تحتوي جملة continue مباشرةً. ويمكننا اعتبار اسم اللافتة اسم كتلة تدفّق التحكم الحلقية. وستنهي جملة continue دورة الحلقة الحالية لكتلة تدفّق التحكم الحلقية مبكراً، حتى لو لم تكن كتلة تدفّق التحكم الحلقية هي الكتلة الحلقية الأكثر داخلية التي تحتوي جملة continue.

فيما يلي مثال على استخدام جملتَي break وcontinue مع لافتات.


package main

import "fmt"

func FindSmallestPrimeLargerThan(n int) int {
Outer:
	for n++; ; n++{
		for i := 2; ; i++ {
			switch {
			case i * i > n:
				break Outer
			case n % i == 0:
				continue Outer
			}
		}
	}
	return n
}

func main() {
	for i := 90; i < 100; i++ {
		n := FindSmallestPrimeLargerThan(i)
		fmt.Print("The smallest prime number larger than ")
		fmt.Println(i, "is", n)
	}
}