المتغيرات وأنواع البيانات
تخيّل أنك تطبخ في المطبخ: المتغيرات هي صناديق تخزين مُعلّمة — الملصق هو الاسم، والصندوق يحتوي البيانات؛ أنواع البيانات هي أشكال الصناديق — الصناديق الدائرية تдерж الحساء، والمربعة تдерж الأطباق، ولا يمكنك خلطها. Go صارمة جداً في إدارة "الصناديق": يجب أن تعلن أي صندوق (نوع) تحتاجه، أي محتوى تضعه (إسناد)، وحتى الصناديق الفارغة لها محتوى افتراضي (قيم صفرية).
1. المفاهيم الأساسية
1.1 إعلان المتغيرات: var مقابل :=
يقدم Go طريقتين لإعلان المتغيرات:
| الطريقة | الصياغة | حالة الاستخدام | النطاق |
|---|---|---|---|
var |
var name int |
متغيرات على مستوى الحزمة، أنواع صريحة | داخل/خارج الدوال |
الإعلان المختصر := |
name := 10 |
متغيرات محلية، استنتاج النوع | داخل الدوال فقط |
الاختلافات الرئيسية:
varيمكن إعلانها بدون إسناد (تستخدم القيمة الصفرية تلقائياً)؛:=تتطلب إسناداً.varيمكن إعلانها خارج الدوال؛:=يمكن استخدامها داخل الدوال فقط.:=اختصار للإعلان + الإسناد؛ يجب أن يكون على الأقل متغير واحد على اليسار جديداً.
1.2 أنواع البيانات الأساسية
| الفئة | النوع | الوصف | مثال |
|---|---|---|---|
| عدد صحيح | int، int8، int16، int32، int64 |
أعداد صحيحة مُوقّعة | var a int = 42 |
| عدد صحيح غير مُوقّع | uint، uint8، uint16، uint32، uint64 |
أعداد غير سالبة | var b uint = 100 |
| عدد عشري | float32، float64 |
أعداد عشرية (الافتراضي float64) |
var c float64 = 3.14 |
| منطقي | bool |
true / false |
var d bool = true |
| نص | string |
تسلسل بايتات غير قابل للتغيير | var e string = "hello" |
| بايت | byte |
اسم مستعار لـ uint8، يمثل بايتاً واحداً |
var f byte = 'A' |
| حرف Unicode | rune |
اسم مستعار لـ int32، يمثل نقطة كود Unicode واحدة |
var g rune = 'ع' |
أنواع تعتمد على المنصة: int و uint هما 32-بت على أنظمة 32-بت و 64-بت على أنظمة 64-بت. في التطوير اليومي، int كافية في معظم الحالات.
1.3 آلية القيم الصفرية
عندما يُعلن متغير في Go بدون إسناد، يتم تهيئته تلقائياً إلى قيمة صفرية، وليس null أو غير معرّف:
| النوع | القيمة الصفرية |
|---|---|
عدد صحيح (int، uint، إلخ) |
0 |
عدد عشري (float32، float64) |
0.0 |
منطقي (bool) |
false |
نص (string) |
"" (نص فارغ) |
| مؤشر، slice، map، channel، دالة، واجهة | nil |
هذه آلية أمان في Go — لن تحصل أبداً على متغير "بلا قيمة"، مما يتجنب العديد من أخطاء المؤشرات الفارغة.
1.4 الثوابت و iota
constيعلن ثوابت قيمها تتحدد وقت الترجمة ولا يمكن تغييرها بعد ذلك.iotaمُولّد ثوابت يتزايد تلقائياً من 0 داخل كتلةconst، مناسب لتعريف التعدادات.
1.5 التحويل بين الأنواع
Go لا تدعم التحويل الضمني بين الأنواع؛ يجب تحويل الأنواع المختلفة بشكل صريح:
var i int = 42
var f float64 = float64(i) // int → float64
var u uint = uint(f) // float64 → uint
1.6 إخراج حزمة fmt المنسق
fmt هي حزمة التنسيق الأكثر استخداماً في Go. الدوال الرئيسية:
| الدالة | الغرض | مثال |
|---|---|---|
fmt.Println |
طباعة مع سطر جديد | fmt.Println("hello") |
fmt.Printf |
طباعة منسقة (بدون سطر جديد) | fmt.Printf("value=%d", 42) |
fmt.Sprintf |
تنسيق إلى نص (بدون طباعة) | s := fmt.Sprintf("%d", 42) |
مواصفات التنسيق الشائعة: %d (عدد صحيح)، %f (عشري)، %s (نص)، %t (منطقي)، %T (نوع)، %v (قيمة عامة)، %q (نص مُقتبس).
2. الصياغة والاستخدام الأساسي
إعلان المتغيرات
// الطريقة 1: إعلان var (داخل/خارج الدوال)
var name string // إعلان، قيمة صفرية ""
var age int = 25 // إعلان مع إسناد
var score = 98.5 // استنتاج النوع كـ float64
// الطريقة 2: الإعلان المختصر := (داخل الدوال فقط)
city := "New York" // إعلان مع إسناد، يُستنتج كـ string
count := 100 // استنتاج النوع كـ int
// إعلان مجمع
var (
width int = 10
height int = 20
area float64
)
var ولم تستخدمه، سيعطي مترجم Go خطأ declared and not used. هذه فلسمة تصميم Go: لا يُسمح بコード ميت. نفس الشيء ينطبق على الإعلان المختصر :=.
إعلان الثوابت
// ثابت واحد
const Pi = 3.14159
const Language string = "Go"
// ثوابت مجمعة + تعداد iota
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
iota صالح فقط داخل كتلة const ويتزايد تلقائياً بمقدار 1 لكل سطر جديد. إذا استُخدم _ لتخطي سطر، فإن iota لا يزال يتزايد.
التحويل بين الأنواع
var i int = 100
var f float64 = float64(i) // تحويل صريح: int → float64
var s string = string(rune(i)) // int → rune → string (ملاحظة: لا يحول الرقم إلى نص)
// الطريقة الصحيحة لتحويل عدد إلى نص:
s2 := fmt.Sprintf("%d", i) // "100"
string(65) ينتج الحرف "A" (كود ASCII)، وليس "65". لتحويل عدد إلى نص عشري، استخدم fmt.Sprintf أو strconv.Itoa.
3. أمثلة الكود
مثال 1: الاستخدام الأساسي (الصعوبة ⭐)
السيناريو: إعلان متغيرات بأنواع مختلفة، وطباعة قيمها وأنواعها.
package main
import "fmt"
func main() {
// ========== إعلان المتغيرات ==========
// 1. إعلان var + نوع صريح
var name string = "Alice"
var age int = 28
// 2. إعلان var + استنتاج النوع (المترجم يستنتج النوع من القيمة)
salary := 15000.5 // يُستنتج كـ float64
// 3. إعلان متغير مختصر (الأكثر شيوعاً)
isEmployed := true
city := "New York"
// 4. عرض القيم الصفرية — إعلان بدون إسناد
var defaultInt int // قيمة صفرية: 0
var defaultFloat float64 // قيمة صفرية: 0
var defaultBool bool // قيمة صفرية: false
var defaultString string // قيمة صفرية: ""
// ========== طباعة المخرجات ==========
fmt.Println("=== معلومات شخصية ===")
fmt.Printf("الاسم: %s (النوع: %T)\n", name, name)
fmt.Printf("العمر: %d (النوع: %T)\n", age, age)
fmt.Printf("الراتب: %.1f (النوع: %T)\n", salary, salary)
fmt.Printf("موظف: %t (النوع: %T)\n", isEmployed, isEmployed)
fmt.Printf("المدينة: %s (النوع: %T)\n", city, city)
fmt.Println("\n=== عرض القيم الصفرية ===")
fmt.Printf("قيمة int الصفرية: %d\n", defaultInt)
fmt.Printf("قيمة float64 الصفرية: %v\n", defaultFloat)
fmt.Printf("قيمة bool الصفرية: %t\n", defaultBool)
fmt.Printf("قيمة string الصفرية: %q\n", defaultString) // %q يُظهر النص مُقتبساً
}
المخرجات:
=== معلومات شخصية ===
الاسم: Alice (النوع: string)
العمر: 28 (النوع: int)
الراتب: 15000.5 (النوع: float64)
موظف: true (النوع: bool)
المدينة: New York (النوع: string)
=== عرض القيم الصفرية ===
قيمة int الصفرية: 0
قيمة float64 الصفرية: 0
قيمة bool الصفرية: false
قيمة string الصفرية: ""
النقاط الرئيسية:
%Qيطبع النوع الأساسي للمتغير.%qيُضيف اقتباسات للنصوص، مما يسهل التمييز بين النصوص الفارغة وغير الفارغة أثناء التصحيح.- آلية القيم الصفرية تضمن أن المتغيرات دائماً لها قيمة محددة، وهو ضمان أمان مهم في Go.
مثال 2: الاستخدام المتوسط (الصعوبة ⭐⭐)
السيناريو: استخدام الثوابت و iota لتعريف التعدادات، والتحويل العملي بين الأنواع.
package main
import "fmt"
// ========== تعداد iota: تعريف صلاحيات الأدوار ==========
type Role int // نوع مخصص مع int كنوع أساسي
const (
Guest Role = iota // 0: زائر
Member // 1: عضو
Admin // 2: مشرف
SuperAdmin // 3: مشرف عام
)
// تنفيذ طريقة String() لـ Role لسهولة الطباعة
func (r Role) String() string {
names := [...]string{"زائر", "عضو", "مشرف", "مشرف عام"}
if int(r) < len(names) {
return names[r]
}
return "دور غير معروف"
}
// ========== استخدام iota المتقدم: أعلام الصلاحيات بالبتات ==========
type Permission int
const (
Read Permission = 1 << iota // 1 (001)
Write // 2 (010)
Execute // 4 (100)
)
// ========== التحويل بين الأنواع عملياً ==========
func main() {
// --- استخدام تعداد iota ---
var userRole Role = Admin
fmt.Printf("دور المستخدم: %s (القيمة: %d)\n", userRole, userRole)
// --- تركيب الصلاحيات بالبتات ---
userPerm := Read | Write // صلاحيات مركبة: قراءة + كتابة = 3 (011)
fmt.Printf("\nقيمة صلاحيات المستخدم: %d (ثنائي: %03b)\n", userPerm, userPerm)
fmt.Printf("هل لديه صلاحية القراءة؟ %t\n", userPerm&Read != 0)
fmt.Printf("هل لديه صلاحية التنفيذ؟ %t\n", userPerm&Execute != 0)
// --- التحويل بين الأنواع ---
var temperature float64 = 36.6
var intTemp int = int(temperature) // float64 → int (يقطع، لا يُقرب)
fmt.Printf("\nدرجة حرارة الجسم: %.1f°م → الجزء الصحيح: %d\n", temperature, intTemp)
// عدد → نص (طريقتان)
var code int = 65
char := string(rune(code)) // الطريقة 1: تحويل إلى حرف Unicode المقابل → "A"
numStr := fmt.Sprintf("%d", code) // الطريقة 2: تحويل إلى نص عشري → "65"
fmt.Printf("العدد %d → الحرف: %s، النص: %q\n", code, char, numStr)
// --- النصوص والبايتات/الأحرف ---
s := "Hello,عالم"
fmt.Printf("\nالنص %q الطول: %d بايت\n", s, len(s))
fmt.Println("الiterate بايت ببايت (غير مُوصى به لـ CJK):")
for i := 0; i < len(s); i++ {
fmt.Printf(" [%d] %d %c\n", i, s[i], s[i])
}
fmt.Println("الiterate حرفاً بحرف (يتعامل بشكل صحيح مع CJK):")
for i, r := range s {
fmt.Printf(" [%d] U+%04X '%c'\n", i, r, r)
}
}
المخرجات:
دور المستخدم: مشرف (القيمة: 2)
قيمة صلاحيات المستخدم: 3 (ثنائي: 011)
هل لديه صلاحية القراءة؟ true
هل لديه صلاحية التنفيذ؟ false
درجة حرارة الجسم: 36.6°م → الجزء الصحيح: 36
العدد 65 → الحرف: A، النص: "65"
النص "Hello,عالم" الطول: 14 بايت
الiterate بايت ببايت (غير مُوصى به لـ CJK):
[0] 72 H
[1] 101 e
[2] 108 l
[3] 108 l
[4] 111 o
[5] 44 ,
[6] 228 ?
[7] 184 ?
[8] 150 ?
[9] 228 ?
[10] 150 ?
[11] 175 ?
الiterate حرفاً بحرف (يتعامل بشكل صحيح مع CJK):
[0] U+0048 'H'
[1] U+0065 'e'
[2] U+006C 'l'
[3] U+006C 'l'
[4] U+006F 'o'
[5] U+002C ','
[6] U+0639 'ع'
[7] U+0627 'ا'
النقاط الرئيسية:
iotaيبدأ من 0 ويتزايد بمقدار 1 لكل سطر، مما يجعله مثالياً لتعريف ثوابت التعداد.- صلاحيات البتات هي تطبيق كلاسيكي لـ
iota: استخدم|لدمج الصلاحيات و&للتحقق من الصلاحيات. int(float64)يقطع الجزء العشرية مباشرة، ولا يُقرب.len(string)يُعيد عدد البايتات، وليس عدد الأحرف. أحرف CJK تشغل 3 بايتات في UTF-8.- استخدم
rangeللiterate على النصوص للتعامل بشكل صحيح مع أحرف متعددة البايتات (runes).
مثال 3: التطبيق الشامل (الصعوبة ⭐⭐⭐)
السيناريو: محاكاة نظام بسيط لمعالجة معلومات تسجيل المستخدمين، يجمع بين المتغيرات والثوابت والتحويل بين الأنواع والإخراج المنسق.
package main
import (
"fmt"
"strings"
)
// ========== الثوابت ==========
// تعداد الجنس
type Gender int
const (
Unknown Gender = iota // 0: غير معروف
Male // 1: ذكر
Female // 2: أنثى
)
func (g Gender) String() string {
return [...]string{"غير معروف", "ذكر", "أنثى"}[g]
}
// مستويات العضوية والخصومات المقابلة
const (
LevelBronze = 1
LevelSilver = 2
LevelGold = 3
LevelPlatinum = 4
)
// استخدام iota لتوليد نسب الخصم
const (
_ = iota // تخطي 0
DiscountBronze = 95 // خصم 5%
DiscountSilver = 90 // خصم 10%
DiscountGold = 85 // خصم 15%
DiscountPlatinum = 80 // خصم 20%
)
// ========== هيكل المستخدم (معاينة، يُشرح بالتفصيل في دروس لاحقة) ==========
type User struct {
Name string
Age int
Gender Gender
Level int
Balance float64
}
// ========== المنطق الأساسي ==========
func main() {
// بيانات مستخدمين محاكاة
users := []User{
{Name: "Li Ming", Age: 25, Gender: Male, Level: LevelGold, Balance: 1580.50},
{Name: "Wang Fang", Age: 30, Gender: Female, Level: LevelPlatinum, Balance: 3200.00},
{Name: "Zhang Wei", Age: 17, Gender: Male, Level: LevelBronze, Balance: 200.00},
{Name: "Zhao Min", Age: 22, Gender: Female, Level: LevelSilver, Balance: 800.75},
}
fmt.Println("╔══════════════════════════════════════════════════════════╗")
fmt.Println("║ نظام تسجيل المستخدمين ║")
fmt.Println("╚══════════════════════════════════════════════════════════╝")
fmt.Println()
// متغيرات الإحصائيات
var totalAge int
var totalBalance float64
var maleCount, femaleCount int
var adultCount int
for _, u := range users {
// الحصول على الخصم
discount := getDiscount(u.Level)
discountAmount := u.Balance * float64(100-discount) / 100.0
// تنسيق إخراج معلومات المستخدم
ageTag := ""
if u.Age >= 18 {
ageTag = "بالغ"
adultCount++
} else {
ageTag = "قاصر"
}
fmt.Printf("%-8s | الجنس: %-6s | العمر: %d (%s) | المستوى: Lv.%d | الرصيد: $%.2f\n",
u.Name, u.Gender, u.Age, ageTag, u.Level, u.Balance)
fmt.Printf(" خصم %d%%، وفور $%.2f\n", discount, discountAmount)
// الإحصائيات
totalAge += u.Age
totalBalance += u.Balance
switch u.Gender {
case Male:
maleCount++
case Female:
femaleCount++
}
fmt.Println(strings.Repeat("-", 60))
}
// طباعة الإحصائيات
avgAge := float64(totalAge) / float64(len(users))
fmt.Println()
fmt.Println("الإحصائيات:")
fmt.Printf(" إجمالي المستخدمين: %d\n", len(users))
fmt.Printf(" ذكور: %d | إناث: %d\n", maleCount, femaleCount)
fmt.Printf(" بالغون: %d | قاصرون: %d\n", adultCount, len(users)-adultCount)
fmt.Printf(" متوسط العمر: %.1f سنة\n", avgAge)
fmt.Printf(" إجمالي الرصيد: $%.2f\n", totalBalance)
fmt.Printf(" متوسط الرصيد: $%.2f\n", totalBalance/float64(len(users)))
// عرض تحويل الأنواع والتنسيق
fmt.Println()
fmt.Println("عرض تحويل الأنواع:")
demoTypeConversion()
}
// getDiscount يُعيد الخصم بناءً على مستوى العضوية
func getDiscount(level int) int {
switch level {
case LevelBronze:
return DiscountBronze
case LevelSilver:
return DiscountSilver
case LevelGold:
return DiscountGold
case LevelPlatinum:
return DiscountPlatinum
default:
return 100 // بدون خصم
}
}
// demoTypeConversion يعرض سيناريوهات مختلفة للتحويل بين الأنواع
func demoTypeConversion() {
// 1. تحويل الأنواع العددية
var pi float64 = 3.14159
var intPi int = int(pi) // يقطع، لا يُقرب
fmt.Printf(" float64 → int: %.5f → %d\n", pi, intPi)
// 2. عدد → نص (مقارنة طريقتين)
var num int = 2024
way1 := string(rune(num)) // حرف Unicode → ليس ما تريده
way2 := fmt.Sprintf("%d", num) // نص عشري → "2024"
way3 := fmt.Sprintf("%x", num) // نص ست عشري → "7e8"
fmt.Printf(" int → string (rune): %q\n", way1)
fmt.Printf(" int → string (%%d): %q\n", way2)
fmt.Printf(" int → string (%%x): %q\n", way3)
// 3. نص → عدد (باستخدام fmt.Sscanf)
var parsed int
_, err := fmt.Sscanf("12345", "%d", &parsed)
if err == nil {
fmt.Printf(" string → int (Sscanf): %d\n", parsed)
}
// 4. bool → string
b := true
boolStr := fmt.Sprintf("%t", b)
fmt.Printf(" bool → string: %t → %q\n", b, boolStr)
// 5. الفرق بين rune و byte
var r rune = 'ع'
var b2 byte = 'A'
fmt.Printf(" rune 'ع': القيمة=%d، سداسي=%U، %d بايت (UTF-8)\n", r, r, len(string(r)))
fmt.Printf(" byte 'A': القيمة=%d، سداسي=%U، 1 بايت\n", b2, b2)
}
المخرجات:
╔══════════════════════════════════════════════════════════╗
║ نظام تسجيل المستخدمين ║
╚══════════════════════════════════════════════════════════╝
Li Ming | الجنس: ذكر | العمر: 25 (بالغ) | المستوى: Lv.3 | الرصيد: $1580.50
خصم 15%، وفور $237.08
------------------------------------------------------------
Wang Fang | الجنس: أنثى | العمر: 30 (بالغ) | المستوى: Lv.4 | الرصيد: $3200.00
خصم 20%، وفور $640.00
------------------------------------------------------------
Zhang Wei | الجنس: ذكر | العمر: 17 (قاصر) | المستوى: Lv.1 | الرصيد: $200.00
خصم 5%، وفور $10.00
------------------------------------------------------------
Zhao Min | الجنس: أنثى | العمر: 22 (بالغ) | المستوى: Lv.2 | الرصيد: $800.75
خصم 10%، وفور $80.08
------------------------------------------------------------
الإحصائيات:
إجمالي المستخدمين: 4
ذكور: 2 | إناث: 2
بالغون: 3 | قاصرون: 1
متوسط العمر: 23.5 سنة
إجمالي الرصيد: $5781.25
متوسط الرصيد: $1445.31
عرض تحويل الأنواع:
float64 → int: 3.14159 → 3
int → string (rune): "\u07e8"
int → string (%d): "2024"
int → string (%x): "7e8"
string → int (Sscanf): 12345
bool → string: true → "true"
rune 'ع': القيمة=1593، سداسي=U+0639، 2 بايت (UTF-8)
byte 'A': القيمة=65، سداسي=U+0041، 1 بايت
النقاط الرئيسية:
- كتل الثوابت مع
iotaيمكنها تعريف التعدادات والقيم المتزايدة بأناقة. - التحويل بين الأنواع يجب أن يكون صريحاً؛ Go لن تُحوّل تلقائياً.
string(int)وfmt.Sprintf("%d", int)لهما تأثيرات مختلفة تماماً — الأولى تنتج حرفاً Unicode، والثانية نصاً عشرياً.runeللتعامل مع أحرف Unicode؛byteللتعامل مع بيانات بايت واحد.%qيُنسّق النصوص مع اقتباسات ويُخزّن الأحرف الخاصة، مفيد جداً للتصحيح.
3. حالات استخدام شائعة
الحالة 1: إدارة التكوين
package main
import "fmt"
// ثوابت تكوين التطبيق (تتحدد وقت الترجمة، غير قابلة للتغيير)
const (
AppName = "GoWeb"
Version = "1.0.0"
MaxRetries = 3
DefaultTimeout = 30 // ثانية
)
// تعداد البيئات
type Env int
const (
Dev Env = iota // تطوير
Staging // اختبار
Prod // إنتاج
)
func (e Env) String() string {
return [...]string{"تطوير", "اختبار", "إنتاج"}[e]
}
func main() {
// متغيرات وقت التشغيل
currentEnv := Prod
dbHost := "192.168.1.100"
dbPort := 3306
debug := currentEnv == Dev
fmt.Printf("التطبيق: %s v%s\n", AppName, Version)
fmt.Printf("البيئة: %s\n", currentEnv)
fmt.Printf("قاعدة البيانات: %s:%d\n", dbHost, dbPort)
fmt.Printf("وضع التصحيح: %t\n", debug)
fmt.Printf("المحاولات القصوى: %d، المهلة: %d ثانية\n", MaxRetries, DefaultTimeout)
}
الحالة 2: تنسيق البيانات والتقارير
package main
import "fmt"
func main() {
// بيانات درجات طلاب محاكاة
type Student struct {
Name string
Score float64
Grade string
}
students := []Student{
{"Alice", 92.5, ""},
{"Bob", 87.0, ""},
{"Charlie", 76.5, ""},
{"Diana", 95.0, ""},
{"Eve", 63.0, ""},
}
// تعيين الدرجات تلقائياً بناءً على الدرجات
for i := range students {
switch {
case students[i].Score >= 90:
students[i].Grade = "A (ممتاز)"
case students[i].Score >= 80:
students[i].Grade = "B (جيد)"
case students[i].Score >= 70:
students[i].Grade = "C (متوسط)"
case students[i].Score >= 60:
students[i].Grade = "D (ناجح)"
default:
students[i].Grade = "F (راسب)"
}
}
// طباعة تقرير الدرجات
fmt.Println("┌─────────┬────────┬────────────┐")
fmt.Println("│ الاسم │ الدرجة│ التقدير │")
fmt.Println("├─────────┼────────┼────────────┤")
var total float64
for _, s := range students {
fmt.Printf("│ %-7s │ %6.1f │ %-10s │\n", s.Name, s.Score, s.Grade)
total += s.Score
}
fmt.Println("├─────────┼────────┼────────────┤")
fmt.Printf("│ المتوسط │ %6.1f │ │\n", total/float64(len(students)))
fmt.Println("└─────────┴────────┴────────────┘")
}
❓ أسئلة شائعة
س1: أيهما أستخدم، var أم :=؟ (ارتباك مفاهيمي)
ج: اتبع هذا المبدأ البسيط:
| السيناريو | المُوصى به | السبب |
|---|---|---|
| إعلان وإسناد داخل الدالة | := |
أكثر اختصاراً، اتفاقية مجتمع Go |
| إعلان بدون إسناد داخل الدالة | var |
:= تتطلب إسناداً |
| متغير على مستوى الحزمة | var |
:= لا يمكن استخدامها خارج الدوال |
| تحديد نوع صريح | var |
تجنب استنتاج نوع غير متوقع |
func example() {
// مُوصى به: استخدم := داخل الدوال
name := "Go"
count := 0
// يجب استخدام var: إعلان بدون إسناد
var result string
if someCondition {
result = "yes"
} else {
result = "no"
}
// يجب استخدام var: نوع دقيق مطلوب
var b byte = 255 // := ستستنتج كـ int
}
س2: لماذا string(65) لا يساوي "65"؟ (مصيدة شائعة)
ج: string(int) تعامل العدد كـ نقطة كود Unicode، وليس كتمثيل نصي للعدد:
// افتراض خاطئ
s1 := string(65) // المتوقع "65"، فعلياً "A" (Unicode U+0041)
// النهج الصحيح
s2 := fmt.Sprintf("%d", 65) // "65"
s3 := strconv.Itoa(65) // "65" (يتطلب import "strconv")
// الاستخدام الحقيقي لـ string(rune): نقطة كود Unicode → حرف
ch := string(rune(1593)) // "ع" (U+0639)
س3: لماذا len("Hello,عالم") لا يساوي 8؟ (مصيدة شائعة)
ج: len() يُعيد عدد البايتات، وليس عدد الأحرف. نصوص Go تستخدم ترميز UTF-8 افتراضياً، والأحرف العربية تشغل 2 بايتات لكل حرف:
s := "Hello,عالم"
fmt.Println(len(s)) // 14 (6 + 4×2 = 14 بايت)
fmt.Println(utf8.RuneCountInString(s)) // 10 (6 + 4 = 10 أحرف)
// الطريقة الصحيحة لعد الأحرف
count := 0
for range s {
count++
}
fmt.Println(count) // 10
س4: كيف أُبدّل متغيرين بسرعة؟ (نصية عملية)
ج: Go يدعم الإسناد المتعدد، سطر واحد من الكود يكفي:
a, b := 10, 20
a, b = b, a // تبديل: a=20, b=10
// يمكنك أيضاً تجاهل قيمة بهذه الطريقة
_, err := someFunction() // استخدم _ لتجاهل قيمة الإرجاع غير المرغوبة
📖 ملخص
- إعلان المتغيرات له طريقتان:
var(عام، يمكن خارج الدوال) و:=(مختصر، داخل الدوال فقط). - Go لديه أنواع أساسية غنية:
int/uint/float64/bool/string/byte/rune. - آلية القيم الصفرية تضمن أن المتغيرات دائماً لها قيمة محددة بعد الإعلان: أعداد
0، منطقيfalse، نص""، مؤشرnil. constيعلن الثوابت؛iotaيتزايد تلقائياً من 0 في كتلconst، مناسب لتعريف التعدادات.- Go لا تدعم التحويل الضمني؛ يجب تحويل الأنواع المختلفة صريحاً، وقد يفقد التحويل الدقة.
- حزمة
fmtأداة قوية للإخراج المنسق:Printlnللطباعة،Printfللتنسيق،Sprintfلتوليد النصوص. len(string)يُعيد عدد البايتات؛ استخدمrangeللiterate على النصوص للتعامل الصحيح مع أحرف متعددة البايتات.string(int)يتعامل مع الأعداد كنقاط كود Unicode، وليس كتحويل عدد إلى نص؛ استخدمfmt.Sprintfلتحويل عدد إلى نص.
📝 تمارين
تمرين 1 (⭐): المتغيرات والقيم الصفرية
أعلن المتغيرات التالية واطبع قيمها الصفرية:
- متغير
intباسمcount - متغير
float64باسمprice - متغير
boolباسمactive - متغير
stringباسمmessage
المخرجات المتوقعة:
count: 0
price: 0
active: false
message: ""
تمرين 2 (⭐⭐): تعداد iota والتحويل بين الأنواع
عرّف نوع Color (أساسي int)، واستخدم iota لتعريف ثلاثة ألوان: Red=0، Green=1، Blue=2. نفّذ طريقة String() لـ Color. ثم:
- أعلن متغير
Colorمضبوطاً علىGreen، واطبع اسمه وقيمته. - حوّل قيمة
float64المقدار3.14159إلىint، واطبع النتيجة (يجب أن تكون3). - حوّل العدد
2025إلى النص"2025"، واطبع النتيجة.
تمرين 3 (⭐⭐⭐): أداة تحويل درجات الحرارة
اكتب برنامجاً يقوم بما يلي:
- يعرّف ثابت
AbsoluteZeroC = -273.15(الصفر المطلق بالدرجة المئوية). - يعلن مجموعة درجات حرارة مئوية:
-40, 0, 37, 100. - يiterate على مصفوفة درجات الحرارة، ويُحوّل كل درجة مئوية إلى فهرنهايت (المعادلة:
F = C × 9/5 + 32). - يُنسّق المخرجات كجدول، بالمتطلبات:
- درجات الحرارة المئوية والفهرنهايت مُقربة إلى خانة عشرية واحدة
- ملاحظة ما إذا كانت درجة الحرارة أقل من الصفر المطلق (مستحيل فيزيائياً)
- محاذاة عرض الأعمدة
المخرجات المتوقعة:
┌─────────┬─────────┬────────────┐
│ مئوية │فهرنهايت│ الحالة │
├─────────┼─────────┼────────────┤
│ -40.0 │ -40.0 │ صالح │
│ 0.0 │ 32.0 │ صالح │
│ 37.0 │ 98.6 │ صالح │
│ 100.0 │ 212.0 │ صالح │
└─────────┴─────────┴────────────┘
تلميح: استخدم const، var (مصفوفة/slice)، حلقة for، تنسيق fmt.Printf، تحويل الأنواع (int ↔ float64).



