Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

الحقول الخاصة

جزء من قسم Object Oriented Programming في رحلة Rust على Coddy — الدرس 10 من 61.

في الدرس السابق، جعلت كل شيء public—الـ struct، وحقوله، ودواله. لكن التغليف (encapsulation) الحقيقي يعني التحكم في كيفية الوصول إلى بياناتك. في Rust، يمكنك جعل struct معيّن public مع إبقاء حقوله private.

لنفترض وجود Wallet لا ينبغي أبداً أن يكون لها رصيد سالب. إذا كان الحقل عاماً (public)، فيمكن لأي شخص تعيينه لأي قيمة:

// wallet.rs
pub struct Wallet {
    balance: f64,  // No pub = خاص!
}

impl Wallet {
    pub fn new(initial: f64) -> Wallet {
        Wallet { balance: initial }
    }
}

لاحظ أن Wallet عام (public)، ولكن balance لا يحتوي على الكلمة المفتاحية pub. وهذا يعني أن الكود خارج الوحدة (module) يمكنه إنشاء Wallet باستخدام المنشئ (constructor)، ولكنه لا يستطيع قراءة أو تعديل حقل balance مباشرة:

// main.rs
mod wallet;
use wallet::Wallet;

fn main() {
    let w = Wallet::new(100.0);
    // println!("{}", w.balance);  // خطأ! الحقل خاص
    // w.balance = -50.0;          // خطأ! لا يمكن الوصول إلى حقل خاص
}

هذا هو أساس التغليف (encapsulation)؛ حيث يتحكم الـ struct في بياناته الخاصة. يجب أن يمر الكود الخارجي عبر الـ methods التي توفرها، مما يعني أنه يمكنك إضافة التحقق من الصحة (validation)، أو التسجيل (logging)، أو أي منطق آخر. وبذلك تظل البيانات محمية من الحالات غير الصالحة.

في الدروس القادمة، ستتعلم كيفية توفير وصول محكم لهذه البيانات الخاصة من خلال دوال getter و setter.

challenge icon

التحدي

سهل

لنقم ببناء نظام مستشعر درجة حرارة يوضح مفهوم التغليف (encapsulation) من خلال الحفاظ على البيانات الحساسة محمية داخل وحدة (module).

ستقوم بإنشاء ملفين لتنظيم الكود الخاص بك:

  • sensor.rs: قم بتعريف هيكل TemperatureSensor يحتوي على حقلين: location (String) و reading (f64). يجب أن يكون الهيكل عاماً (public) بحيث يمكن استخدامه من ملفات أخرى، ولكن اجعل كلا الحقلين خاصين (private) — لا ينبغي للكود الخارجي أن يتمكن من الوصول مباشرة إلى بيانات المستشعر أو تعديلها. أضف منشئاً (constructor) عاماً باسم new يأخذ اسم الموقع وقراءة درجة الحرارة الأولية.
  • main.rs: قم باستيراد وحدة المستشعر الخاصة بك وأنشئ TemperatureSensor باستخدام المنشئ. نظراً لأن الحقول خاصة، فلن تتمكن من طباعتها مباشرة — بدلاً من ذلك، قم بطباعة رسالة تأكيد توضح أنه تم إنشاء المستشعر بنجاح.

يركز هذا التحدي على المفهوم الأساسي للحقول الخاصة: الهيكل متاح للوصول، لكن بياناته الداخلية محمية. يمكن للكود الخارجي التفاعل مع المستشعر فقط من خلال الطرق (methods) التي توفرها (في هذه الحالة، المنشئ فقط).

يجب أن يتبع مخرجك هذا التنسيق تماماً:

Sensor created for: {location}

للوصول إلى الموقع من أجل الطباعة، ستحتاج إلى جعل دالة new تطبع هذه الرسالة قبل إرجاع المستشعر، لأن الحقل نفسه خاص.

على سبيل المثال، إذا قمت بإنشاء مستشعر بموقع "Kitchen" وقراءة 22.5، فسيكون المخرج:

Sensor created for: Kitchen

ستتلقى مدخلين: اسم الموقع وقراءة درجة الحرارة الأولية.

ورقة مرجعية

في لغة Rust، يمكنك جعل الهيكل (struct) عاماً مع إبقاء حقوله خاصة لتحقيق مبدأ التغليف (encapsulation). هذا يمنع الكود الخارجي من الوصول مباشرة إلى البيانات الداخلية أو تعديلها.

لجعل الحقل خاصاً، ببساطة قم بحذف الكلمة المفتاحية pub:

pub struct Wallet {
    balance: f64,  // No pub = private field
}

impl Wallet {
    pub fn new(initial: f64) -> Wallet {
        Wallet { balance: initial }
    }
}

في هذا المثال، Wallet عام (يمكن استخدامه خارج الوحدة)، ولكن balance خاص (لا يمكن الوصول إليه مباشرة من الخارج).

يمكن للكود الخارجي إنشاء مثيلات (instances) باستخدام دوال الإنشاء العامة، ولكن لا يمكنه قراءة أو تعديل الحقول الخاصة:

mod wallet;
use wallet::Wallet;

fn main() {
    let w = Wallet::new(100.0);
    // println!("{}", w.balance);  // Error! field is private
    // w.balance = -50.0;          // Error! can't access private field
}

يضمن هذا التغليف أن الهيكل (struct) يتحكم في بياناته الخاصة، ويجب على الكود الخارجي استخدام الدوال التي توفرها للتفاعل معه.

جرّب بنفسك

mod sensor;

use sensor::TemperatureSensor;

fn main() {
    // قراءة المدخلات
    let mut location = String::new();
    std::io::stdin().read_line(&mut location).expect("Failed to read line");
    let location = location.trim().to_string();
    
    let mut reading_str = String::new();
    std::io::stdin().read_line(&mut reading_str).expect("Failed to read line");
    let reading: f64 = reading_str.trim().parse().expect("Failed to parse reading");
    
    // TODO: إنشاء TemperatureSensor باستخدام المنشئ (constructor)
    // ستقوم الدالة new بمعالجة طباعة رسالة التأكيد
    
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming