Go: متغيرات Go وأنواع البيانات
آخر تحديث: 2026-08-26
المتغيرات هي حجر الزاوية في أي برنامج — وتستخدم لغة Go الحد الأدنى من القواعد النحوية لجعل إعلانات المتغيرات واضحة وآمنة في آن واحد.
فلسفة التصميم الكامنة وراء نظام الأنواع في لغة Go: الصراحة بدلاً من الضمنية. وهذا يعني أن كل متغير له نوع محدد بوضوح، مما يتيح للمترجم اكتشاف أكثر من 90% من الأخطاء في مرحلة الترجمة. في هذا الدرس، ستتعلم جميع قواعد إعلان المتغيرات في لغة Go.
1. ستتعلم
- الطريقتان لإعلان المتغيرات:
varو:= - أنواع البيانات الأساسية الـ 14 في لغة Go
- آلية «القيمة الصفرية» الفريدة في لعبة «غو»
- إعلانات
constالثوابت وiotaالقوائم - التحويل الصريح للأنواع (لا تسمح لغة Go بالتحويلات الضمنية)
- إخراج بتنسيق
fmt.Printf/fmt.Sprintf - أمثلة شاملة لنماذج تسجيل المستخدمين
2. القصة الحقيقية لمهندس متكامل
(1) المشكلة: تصحيح الأخطاء في ساعات متأخرة من الليل بسبب الكتابة الديناميكية
أليس مهندسة انتقلت من لغة «بايثون» إلى لغة «Go». وقد انضمت مؤخرًا إلى الشركة وتولت مسؤولية وحدة تسجيل المستخدمين:
«قال مدير المشروع: "بعد الإطلاق، كانت بيانات المستخدمين في حالة فوضى تامة. فقد أدخل بعضهم 1 يناير 2024 كتاريخ ميلادهم، وأدخل آخرون "أمس"، بينما أدخل آخرون
null— ولم يكن بالإمكان تحليل نصف البيانات الموجودة في قاعدة البيانات."»
ألقت نظرة على الكود واكتشفت أن «الكتابة الديناميكية» في لغة بايثون هي السبب:
# Python: variable types can change at any time
birthday = "1990-01-01"
birthday = 1990 # integer suddenly valid
birthday = None # None also valid
birthday = ["today"] # list also valid!
يمكن أن يكون المتغير نفسه سلسلة نصية، أو عددًا صحيحًا، أو None، أو قائمة في أجزاء مختلفة من الكود — ولا توجد آلية لمنع هذا الالتباس.
(2) الحل بلغة Go
أعادت أليس كتابة نموذج المستخدم بلغة Go:
// user.go
package main
import "fmt"
type User struct {
Name string // string
Age int // integer
Height float64 // float
IsActive bool // boolean
Birthday string // ISO 8601 date string
}
func main() {
user := User{
Name: "Alice",
Age: 28,
Height: 165.5,
IsActive: true,
Birthday: "1996-05-12",
}
// Want to change `Age` to a string? You'll get a compile-time error!
// user.Age = "twenty-eight" // ❌ cannot use "twenty-eight" as int
fmt.Printf("User: %s, Age: %d, Height: %.1fcm\n",
user.Name, user.Age, user.Height)
}
الناتج:
User: Alice, Age: 28, Height: 165.5cm
(3) المزايا: يكتشف 90% من الأخطاء أثناء التحويل البرمجي
| البعد | بايثون (الكتابة الديناميكية) | Go (الكتابة الثابتة والقوية) |
|---|---|---|
| التحقق من الأنواع في وقت الترجمة | ❌ لا شيء | ✅ صارم |
| خطأ في نوع البيانات أثناء التشغيل | متكرر | نادر |
| الإكمال التلقائي في IDE | ضعيف | قوي |
| أمان إعادة الهيكلة | منخفض | مرتفع |
| فخ القيمة الفارغة (لا شيء/فارغة) | شائع | حماية آلية القيمة الصفرية |
3. إعلان المتغيرات: var و:=
توفر لغة Go طريقتين لإعلان المتغيرات: الكلمة الرئيسية var (الطريقة القياسية) والإعلان المختصر := (الطريقة المختصرة).
(1) إعلان الكلمة الرئيسية var (الطريقة القياسية)
يمكن استخدام var داخل الدوال أو على مستوى الحزمة، وهناك ثلاث طرق لكتابته:
// Method 1: Declare a variable with an explicit type
var name string = "Alice"
// Method 2: Declare a variable and omit the type (the type is inferred from the value)
var name = "Alice"
// Method 3: Declare only; initialize to zero value
var name string // name = ""
▶ مثال: مقارنة بين ثلاث طرق لكتابة var
package main
import "fmt"
func main() {
// Method 1: Explicit Type + Initialization
var userName string = "Alice"
var userAge int = 28
// Method 2: Type Inference
var userEmail = "alice@example.com"
// Method 3: Declare only; rely on the default value
var userBio string // ""
fmt.Println(userName, userAge, userEmail, userBio)
}
الناتج:
Alice 28 alice@example.com
(3) := الإعلان المختصر (الأكثر شيوعًا)
:= هو «سكر نحوي» في لغة Go؛ ولا يمكن استخدامه إلا داخل الدوال، كما أنه يستنتج النوع تلقائيًا:
package main
import "fmt"
func main() {
// Short declaration: variable_name := value
name := "Alice" // inferred as string
age := 28 // inferred as int
height := 165.5 // inferred as float64
isActive := true // inferred as bool
fmt.Printf("%s is %d years old, height %.1f, active: %v\n",
name, age, height, isActive)
}
الناتج:
Alice is 28 years old, height 165.5, active: true
(4) var أم vs := — أيهما يجب أن أختار؟
| السيناريو | النهج الموصى به | السبب |
|---|---|---|
| المتغيرات على مستوى الحزمة | var |
:= لا يمكن استخدامها خارج الدوال |
| المتغيرات المحلية داخل الدالة | := |
موجزة؛ توفر 4 أحرف |
| يجب أن تكون القيمة صفرًا | var x int |
:= يجب تهيئتها في نفس الوقت |
| يتطلب تحويلًا صريحًا للنوع | var x int = int(3.14) |
يظهر تحويل النوع بوضوح |
| الإعلانات المجمعة | var (...) |
:= لا يدعم الإعلانات المجمعة |
4. أنواع البيانات الأساسية في لغة Go
تحتوي لغة «Go» على 14 نوعًا أساسيًا من أنواع البيانات، والتي تنقسم إلى أربع فئات رئيسية بناءً على الغرض منها.
(1) أنواع الأعداد الصحيحة
graph TB
A[Integer Types] --> B[Signed int]
A --> C[Unsigned uint]
A --> D[Byte type]
A --> E[Special rune]
B --> B1[int<br/>at least 32 bits]
B --> B2[int8 / int16 / int32 / int64]
C --> C1[uint<br/>at least 32 bits]
C --> C2[uint8 / uint16 / uint32 / uint64]
D --> D1[byte = uint8<br/>emphasizes byte]
E --> E1[rune = int32<br/>emphasizes Unicode code point]
| النوع | الحجم | النطاق | الاستخدامات النموذجية |
|---|---|---|---|
int |
32 بت أو 64 بت (حسب النظام الأساسي) | من -2^31 إلى 2^31-1 أو أكبر | نوع عدد صحيح افتراضي |
int8 |
8 بت | من -128 إلى 127 | أعداد صحيحة ذات نطاق صغير |
int32 |
32 بت | من -2^31 إلى 2^31-1 | حجم ثابت عبر الأنظمة المختلفة |
int64 |
64 بت | من -2^63 إلى 2^63-1 | الأعداد الصحيحة الكبيرة، الطوابع الزمنية |
uint |
32 بت أو 64 بت | من 0 إلى 2^32-1 أو أكثر | غير موقّع افتراضيًا |
byte |
8 بت | 0–255 | بايت، حرف ASCII |
rune |
32 بت | نقطة رمز يونيكود | حرف يونيكود واحد |
▶ مثال: الفرق بين int وint64
package main
import (
"fmt"
"unsafe"
)
func main() {
var a int = 100
var b int64 = 200
fmt.Printf("a (int) size: %d bytes\n", unsafe.Sizeof(a))
fmt.Printf("b (int64) size: %d bytes\n", unsafe.Sizeof(b))
// You cannot directly add `int` and `int64`!
// sum := a + b // ❌ mismatched types int and int64
// An explicit conversion is required
sum := a + int(b)
fmt.Println("sum:", sum)
}
الناتج (على الأنظمة ذات 64 بت):
a (int) size: 8 bytes
b (int64) size: 8 bytes
sum: 300
(3) أنواع الأرقام العائمة
| النوع | الحجم | الدقة | الاستخدامات النموذجية |
|---|---|---|---|
float32 |
32 بت | ~7 أرقام معنوية | الحوسبة العلمية، الرسومات |
float64 |
64 بت | ~15 رقمًا معنويًا | نوع العدد العائم الافتراضي |
(4) النوع المنطقي
var isActive bool = true
var hasPermission bool // zero value = false
(5) نوع السلسلة
var greeting string = "Hello, Go!"
var emptyString string // zero value = "" (empty string)
var multiLine string = `Line 1
Line 2
Line 3` // backticks support multi-line strings
(6) جدول مرجعي سريع لـ 14 نوعًا
| الفئة | النوع | القيمة الصفرية |
|---|---|---|
| عدد صحيح | int / int8 / int16 / int32 / int64 |
0 |
| أعداد صحيحة غير موقعة | uint / uint8 / uint16 / uint32 / uint64 |
0 |
| بايت/حرف | byte (=uint8) / rune (=int32) |
0 |
| النقطة العائمة | float32 / float64 |
0.0 |
| منطقية | bool |
كاذب |
| سلسلة | string |
"" |
5. آلية «القيمة الصفرية» الفريدة في لعبة «غو»
لا توجد متغيرات «غير مُهيأة» في لغة Go — فكل متغير له قيمة افتراضية معقولة عند إعلانه.
▶ مثال: القيم الصفرية لجميع الأنواع
package main
import "fmt"
func main() {
var i int
var f float64
var b bool
var s string
var p *int // pointer zero value is nil
fmt.Printf("int: %d\n", i)
fmt.Printf("float64: %f\n", f)
fmt.Printf("bool: %v\n", b)
fmt.Printf("string: %q\n", s) // %q shows quoted string, empty string shows ""
fmt.Printf("pointer: %v\n", p)
}
الناتج:
int: 0
float64: 0.000000
bool: false
string: ""
pointer: <nil>
(2) القيم الصفرية مقارنة باللغات الأخرى
| النوع | القيمة الصفرية | جافا | بايثون | C/C++ |
|---|---|---|---|---|
| عدد صحيح | 0 | 0 | غير موجود | غير مُعرَّف (قيمة غير صالحة) |
| العدد العائم | 0.0 | 0.0 | غير موجود | غير محدد |
| منطقية | خطأ | خطأ | خطأ | غير محدد |
| سلسلة | "" | null | "" | غير مُعرَّف |
| مؤشر | nil | null | None | NULL (ولكنه عرضة لحدوث مؤشرات غير صحيحة) |
6. const الثوابت وiota القوائم
(1) استخدم const لإعلان الثوابت
يتم تحديد الثوابت في مرحلة الترجمة، ولا يمكن تعديلها، ويجب أن تكون من الأنواع الأساسية:
const Pi = 3.14159
const AppName = "web-tutorial"
const MaxConnections = 1000
(2) التعداد في لغة Iota: التعداد الأنيق في لغة Go
iota هو عداد ثابت في لعبة «غو» يبدأ من 0 ويزداد تلقائيًا بمقدار 1 في كل سطر:
package main
import "fmt"
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
func main() {
fmt.Printf("Sunday=%d Monday=%d Saturday=%d\n", Sunday, Monday, Saturday)
}
الناتج:
Sunday=0 Monday=1 Saturday=6
▶ مثال: زيادة مخصصة بقيمة «iota» (رموز حالة HTTP)
package main
import "fmt"
const (
StatusOK = iota * 100 // 0
StatusRedirect // 100
StatusClientError // 200
StatusServerError // 300
)
func main() {
fmt.Printf("OK=%d Redirect=%d ClientError=%d ServerError=%d\n",
StatusOK, StatusRedirect, StatusClientError, StatusServerError)
}
الناتج:
OK=0 Redirect=100 ClientError=200 ServerError=300
(4) الاستخدامات المتقدمة لـ iota: أقنعة البتات (أذونات الملفات)
package main
import "fmt"
const (
ReadPermission = 1 << iota // 1 << 0 = 1
WritePermission // 1 << 1 = 2
ExecutePermission // 1 << 2 = 4
)
func main() {
// Permission Combination: Read + Write = 1 | 2 = 3
rw := ReadPermission | WritePermission
fmt.Printf("Read=%d Write=%d Execute=%d RW=%d\n",
ReadPermission, WritePermission, ExecutePermission, rw)
}
الناتج:
Read=1 Write=2 Execute=4 RW=3
iota هي الصيغة الوحيدة في لغة Go التي تقترب من مفهوم «enum»، لكنها ليست صارمة بقدر أنواع enum في لغتي Python أو Java. إذا كنت بحاجة إلى أمان أنواع أقوى في بيئة الإنتاج، فيمكنك تعريف أنواعك الخاصة.
7. التحويل الصريح للأنواع
لا تسمح لغة Go بالتحويلات الضمنية للأنواع — يجب الإعلان عن جميع التحويلات بشكل صريح. وهذا يعكس مبدأ «الصريح قبل الضمني» الذي تتبعه لغة Go.
(1) تحويل أنواع الأرقام
package main
import "fmt"
func main() {
var i int = 42
var f float64 = float64(i) // int → float64
var u uint = uint(f) // float64 → uint
fmt.Printf("i=%d f=%v u=%d\n", i, f, u)
// Truncation example: Converting a float to an int results in the loss of the decimal part
var pi float64 = 3.14159
var truncated = int(pi)
fmt.Printf("pi=%.5f truncated=%d\n", pi, truncated)
}
الناتج:
i=42 f=42 u=42
pi=3.14159 truncated=3
(2) تحويل السلاسل: حزمة strconv
package main
import (
"fmt"
"strconv"
)
func main() {
// String → Number
n, err := strconv.Atoi("123")
fmt.Printf("n=%d err=%v\n", n, err)
// Number → String
s := strconv.Itoa(456)
fmt.Printf("s=%s (type: %T)\n", s, s)
// String → float
f, _ := strconv.ParseFloat("3.14", 64)
fmt.Printf("f=%f\n", f)
// float → string (specified precision)
fmt.Println(strconv.FormatFloat(3.14159, 'f', 2, 64)) // "3.14"
}
الناتج:
n=123 err=<nil>
s=456 (type: string)
f=3.140000
3.14
(3) التحويل الضمني مقابل التحويل الصريح
| السيناريو | جافا/C++ | Go |
|---|---|---|
int + float |
تم تحويلها تلقائيًا إلى نوع float | خطأ في الترجمة؛ يجب أن يكون التحويل صريحًا |
int32 + int64 |
تم تحويلها تلقائيًا إلى int64 | خطأ في الترجمة |
int إلى string |
ضمني | يتطلب strconv.Itoa() |
8. fmt: تنسيق الإخراج
(1) أفعال التنسيق الشائعة
| الفعل | المعنى | الأنواع التي ينطبق عليها |
|---|---|---|
%d |
عدد صحيح عشري | int، int8/16/32/64 |
%f |
عدد عائم | float32، float64 |
%s |
سلسلة | سلسلة |
%v |
التنسيق الافتراضي لأي نوع | أي نوع |
%T |
النوع | أي نوع |
%t |
منطقية | bool |
%q |
سلسلة مقتبسة | سلسلة، رون |
%x |
سداسي عشري | عدد صحيح، [] بايت |
%p |
عنوان المؤشر | المؤشر |
%% |
علامة النسبة المئوية الحرفية | — |
▶ مثال: عرض توضيحي شامل لـ fmt.Printf
package main
import "fmt"
func main() {
name := "Alice"
age := 28
height := 165.5
isActive := true
hobbies := []string{"coding", "reading", "hiking"}
fmt.Printf("Name: %s\n", name) // %s string
fmt.Printf("Age: %d\n", age) // %d integer
fmt.Printf("Height: %.1f cm\n", height) // %.1f float with 1 decimal
fmt.Printf("Active: %t\n", isActive) // %t boolean
fmt.Printf("Hobbies: %v\n", hobbies) // %v default format
fmt.Printf("Name type: %T\n", name) // %T type
fmt.Printf("100%% complete!\n") // %% literal %
// %v Advanced usage: %+v displays the field name (struct), %#v displays Go syntax
fmt.Printf("hobbies: %#v\n", hobbies)
}
الناتج:
Name: Alice
Age: 28
Height: 165.5 cm
Active: true
Hobbies: [coding reading hiking]
Name type: string
100% complete!
hobbies: []string{"coding", "reading", "hiking"}
(3) الفرق بين Print و Println و Printf
| الوظيفة | إنهاء الإخراج | حالات الاستخدام |
|---|---|---|
fmt.Print() |
بدون فاصل أسطر | إخراج متواصل |
fmt.Println() |
فواصل أسطر تلقائية + مفصولة بمسافات | تصحيح أخطاء بسيط |
fmt.Printf() |
لا يضيف سطراً جديداً؛ يتطلب \n |
إخراج منسق |
fmt.Sprintf() |
تُرجع سلسلة نصية؛ ولا تُعرض | تربط السلاسل النصية |
9. مثال كامل: نموذج تسجيل المستخدم
اجمع كل المفاهيم الأساسية من هذا الدرس واكتب برنامجًا لجمع بيانات تسجيل المستخدمين:
// main.go
package main
import (
"fmt"
"strconv"
)
// Defining User Role Enumerations Using Iota
const (
RoleGuest = iota // 0 Guest
RoleUser // 1 Regular User
RoleAdmin // 2 Admin
RoleSuperAdmin // 3 Super Admin
)
func main() {
// 1. Use the := short declaration for user information
name := "Alice"
age := 28
height := 165.5
isActive := true
role := RoleUser
// 2. Explicit Type Conversion
ageAsFloat := float64(age)
heightAsString := strconv.FormatFloat(height, 'f', 1, 64)
// 3. const Constants
const MaxLoginAttempts = 5
// 4. fmt: Formatted Output
fmt.Println("========================================")
fmt.Println(" User Registration Form")
fmt.Println("========================================")
fmt.Printf("Name: %s\n", name)
fmt.Printf("Age: %d (%.0f years old)\n", age, ageAsFloat)
fmt.Printf("Height: %s cm\n", heightAsString)
fmt.Printf("Active: %t\n", isActive)
fmt.Printf("Role: %d (User)\n", role)
fmt.Printf("Max Attempts: %d\n", MaxLoginAttempts)
fmt.Printf("Type Check: name is %T, age is %T\n", name, age)
fmt.Println("========================================")
// 5. Demonstration of Zero Values for Variables
var uninitializedAge int
fmt.Printf("\nUninitialized age (zero value): %d\n", uninitializedAge)
}
النتيجة المتوقعة:
========================================
User Registration Form
========================================
Name: Alice
Age: 28 (28 years old)
Height: 165.5 cm
Active: true
Role: 1 (User)
Max Attempts: 5
Type Check: name is string, age is int
========================================
Uninitialized age (zero value): 0
strconv.Atoi("abc") خطأً، وهو لا يجب تجاهله. الصيغة الصحيحة: n, err := strconv.Atoi("abc"); if err != nil { ... }. يتعمق الدرس 8 في موضوع معالجة الأخطاء.
❓ أسئلة شائعة
var أم :=؟:= داخل الدوال — فهو موجز ويفرض التهيئة؛ أما المتغيرات على مستوى الحزمة فيجب أن تستخدم var. إن البايت كود الناتج عن كليهما متطابق تمامًا؛ فهي مجرد صيغة مبسطة.int في لغة Go باختلاف المنصات بين 32 بت و64 بت. ما هي المشكلات التي قد يسببها ذلك؟int32 أو int64 صراحةً لتجنب مشكلات التوافق بين الأنظمة المختلفة.enum. كيف يمكن إنشاء قائمة عدّية؟const + iota. ورغم أنها ليست صارمة بقدر enum class في لغة Java، إلا أنها كافية. وإذا كنت بحاجة إلى أمان الأنواع، فيمكنك استخدام نوع مخصص مثل type Role int إلى جانب مجموعة من الثوابت.iota من 0. هل يمكن أن تبدأ من 1؟_ = iota لتخطي 0، أو iota + 1: const (A = iota + 1; B; C) مما ينتج عنه A=1، B=2، C=3. ويُستخدم هذا عادةً في الحالات التي تحتاج فيها القيم إلى البدء من 1، مثل رموز حالة HTTP والأشهر.int وlong). ويجعل التحويل الصارم للأنواع في لغة Go الكود أكثر قابلية للقراءة ويؤدي إلى تقليل عدد الأخطاء.string و[]byte؟[]byte(s) بتحويل سلسلة أحرف إلى شريحة بايت (مع نسخ البيانات)؛ بينما تقوم string(b) بتحويل شريحة بايت إلى سلسلة أحرف. وتُستخدم هاتان الوظيفتان بشكل شائع في سيناريوهات الإدخال/الإخراج الشبكي والتشفير. ملاحظة: []byte مناسبة للتعديل، بينما string مناسبة للاستخدام للقراءة فقط.&Pi إلى حدوث خطأ في وقت التحويل البرمجي.📖 ملخص
- في لغة Go، يمكن إعلان المتغيرات باستخدام إما
varأو:=. يُنصح باستخدام:=داخل الدوال، بينما يُشترط استخدامvarعلى مستوى الحزمة. - 14 نوعًا أساسيًا مقسمة إلى 4 فئات: الأعداد الصحيحة (int/int8–64)، والأعداد العائمة (float32/64)، والقيم المنطقية، والسلاسل
- آلية القيمة الصفرية هي إحدى ميزات الأمان في لغة Go: فكل متغير له قيمة افتراضية معقولة، مما يمنع وجود المؤشرات غير المحددة.
constيُعلن الثوابت؛iotaيوفر طريقة أنيقة للتعداد (رموز حالة HTTP، أقنعة البتات)- تحظر لغة Go التحويلات الضمنية للأنواع؛ بل يجب أن تكون صريحة (
int(x)/strconv.Itoa()) - يستخدم
fmt.Printfمحددات التنسيق (%d،%f،%s،%v،%T،%q،%t) للتحكم في الإخراج - لغة «Go» الثابتة وذات الأنواع القوية تكتشف 90% من الأخطاء في مرحلة التحويل البرمجي، مما يجعلها حجر الزاوية في عملية تطوير البرمجيات التعاونية
📝 تمارين
-
تمرين أساسي (مستوى الصعوبة ⭐): قم بتعريف 5 متغيرات (name/age/height/isStudent/gpa) باستخدام كل من صيغتي
varو:=، ثم اطبع قيمها وأنواعها. -
تمرين متقدم (درجة الصعوبة ⭐⭐): استخدم
iotaلتعريف أربعة ثوابت لرموز حالة HTTP (200/404/500/503)، واكتب دالةhandleRequest(code int)تُرجع النص المقابل (على سبيل المثال، "OK"، "Not Found"، "Internal Server Error"، أو "Service Unavailable") بناءً على رمز الحالة. -
مشكلة التحدي (الصعوبة ⭐⭐⭐): اكتب محول وحدات: خذ درجة حرارة بالفهرنهايت كمدخل، وأخرج درجة الحرارة بالسيلزيوس (الصيغة: C = (F - 32) × 5/9). المتطلبات: (1) استخدم
fmt.Scanfلقراءة المدخلات؛ (2) استخدمstrconv.ParseFloatلتحويل النوع؛ (3) تعامل مع المدخلات غير الصحيحة (أرجع خطأً).