English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

المباني في Rust

في Rust، يمكن للهيكلية (Struct) والجملة (Tuple) جمع عدة أنواع من البيانات المختلفة في كيان واحد، ولكن لكل عضو في الهيكلية ولهيكلية نفسها اسم، مما يجعل من السهل الوصول إلى أعضاء الهيكلية دون الحاجة إلى تذكر المؤشرات. تستخدم الجملة غالبًا لنقل قيم متعددة غير معرفة مسبقًا، بينما تستخدم الهيكلية لتحديد هيكل البيانات المستخدم بشكل متكرر. يُدعى كل عضو في الهيكلية "حقل".

تعريف هيكلية الهيكل

هذا هو تعريف هيكلية الهيكل:

struct Site {
    domain: String ,
    name: String ,
    nation: String ,
    found: u32
}

ملاحظة: إذا كنت تستخدم C/C++ بشكل متكرر، تذكر أن جملة struct في Rust تستخدم فقط للتعريف وليس للإعلان عن مثال، وأنها لا تحتاج إلى علامة النهاية ;، وكل حقل تعريف يجب أن يكون منفصلًا عن الآخر بـ ,.

مثال هيكلية الهيكل

تأثرت Rust في العديد من الأماكن بـ JavaScript، عند إنشاء هيكلية الهيكل باستخدام جملة key: value من JSON Object لتحقيق التعريف:

دع w3codebox = Site {
    domain: String::from("ar.oldtoolbag.com"),
    name: String::from("w3codebox"),
    nation: String::from("China"),
    found: 2013
};

إذا لم تكن تعرف على JSON Object، يمكنك تجاهله، فقط تذكر النمط:

اسم فئة الهيكل {
    اسم الحقل : قيمة الحقل ,
    ...
}

هذا الميزة تجعل البرنامج أكثر وضوحًا ولا تحتاج إلى إدخال قيم الأعضاء وفقًا للترتيب المحدد.

إذا كان هناك حقلًا في الهيكلية الذي يحمل الاسم نفسه كاسم المتغير الموجود، يمكن تبسيط الكتابة: }}

دع المجال = String::from("ar.oldtoolbag.com");
دع الاسم = String::from("w3codebox");
دع w3codebox = Site {
    domain,          // تساوي domain : domain,
    name,          // تساوي name : name,
    nation: String::from("China"),
    traffic: 2013
};

هناك حالة: تريد إنشاء مثال على هيكلية جديدة، حيث يجب أن تكون معظم الخصائص متشابهة مع هيكلية أخرى موجودة، ولكن يجب تغيير قيمة حقل واحد أو اثنين فقط، يمكنك استخدام جملة تحديث الهيكل:

دع الموقع = Site {
    domain: String::from("ar.oldtoolbag.com"),
    name: String::from("w3codebox"),
    ..w3codebox
};

ملاحظة: لا يجب أن تكون هناك coma بعد ..w3codebox. هذه الجملة لا تسمح بنسخ مثال هيكلية آخر دون تغيير أي قيمة في الحقل، مما يعني أن يجب تغيير قيمة على الأقل في الحقل لتشير إلى قيمة أخرى من المثال.

هيكلية التجميع

هناك طريقة بسيطة أكثر للتعريف واستخدام الهياكل:هيكلية التجميع.

هيكلية التجميع هي هيكلية تشبه التجميع.

الفرق بينه وبين التجميع هو أنه يحتوي على أسماء ونمط نوع ثابت. وجوده يعني أن يتم التعامل مع بيانات بسيطة تحتاج إلى تعريف نوعها (يستخدم غالبًا) وليس معقدًا جدًا:

struct Color(u8, u8, u8);
struct Point(f64, f64);
دع الأسود = Color(0, 0, 0);
دع الأصل = Point(0.0, 0.0);

"اللون" و "محدد النقطة" هما نوعان شائعان من البيانات، ولكن إذا كتبت مفتاح تعريف الكائن بباقة زوايا مزدوجة وأسماء اثنين لتحسين القدرة على القراءة، فإنه يتم التضحية بالسرعة، Rust لن يترك هذا المسألة. يمكن استخدام كائنات هيكلية مركبة بنفس الطريقة التي يستخدم بها التجميع، من خلال . والمسافات البعيدة للاستعلام:

fn main() {
    struct Color(u8, u8, u8);
    struct Point(f64, f64);
    دع الأسود = Color(0, 0, 0);
    دع الأصل = Point(0.0, 0.0);
    اطبع!("الأسود = (({}, {}, {}),")، الأسود.0، الأسود.1، الأسود.2);
    اطبع!("الأصل = (({}, {}),")، الأصل.0، الأصل.1);
}

نتيجة التنفيذ:

الأسود = (0, 0, 0)
origin = (0, 0)

结构体所有权

结构体必须掌握字段值所有权,因为结构体失效的时候会释放所有字段。

这就是为什么本章的案例中使用了 String 类型而不使用 &str 的原因。

但这不意味着结构体中不定义引用型字段,这需要通过"生命周期"机制来实现。

但现在还难以说明"生命周期"概念,所以只能在后面章节说明。

输出结构体

调试中,完整地显示出一个结构体示例是非常有用的。但如果我们手动的书写一个格式会非常的不方便。所以 Rust 提供了一个方便地输出一整个结构体的方法:

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}
fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };
    println!("rect1 {:?}", rect1);
}

如第一行所示:一定要导入调试库 #[derive(Debug)] ,之后在 println 和 print 宏中就可以用 {:?} 占位符输出一整个结构体:

rect1 is Rectangle { width: 30, height: 50 }

如果属性较多的话可以使用另一个占位符 {:#?} 。

输出结果:

rect1 is Rectangle {
    width: 30,
    height: 50
}

结构体方法

方法(Method)和函数(Function)类似,只不过它是用来操作结构体示例的。

如果你学习过一些面向对象的语言,那你一定很清楚函数一般放在类定义里并在函数中用 this 表示所操作的示例。

Rust 语言不是面向对象的,从它所有权机制的创新可以看出这一点。但是面向对象的珍贵思想可以在 Rust 实现。

结构体方法的第一个参数必须是 &self,不需声明类型,因为 self 不是一种风格而是关键字。

计算一个矩形的面积:

struct Rectangle {
    width: u32,
    height: u32,
}
    
impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}
fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };
    println!("rect1's area is {}", rect1.area());
}

输出结果:

rect1's area is 1500

请注意,在调用结构体方法的时候不需要填写 self ,这是出于对使用方便性的考虑。

一个多参数的实例:

struct Rectangle {
    width: u32,
    height: u32,
}
impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
    fn wider(&self, rect: &Rectangle) -> bool {
        self.width > rect.width
    }
}
fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };
    let rect2 = Rectangle { width: 40, height: 20 };
    println!("{}", rect1.wider(&rect2));
}

نتيجة التنفيذ:

false

هذا البرنامج يحسب ما إذا كان rect1 أعرض من rect2.

وظيفة مرتبطة بنية

السبب في أن "وظيفة بنية المباني" لا تسمى "وظيفة بنية المباني" هو أن اسم "وظيفة" محفوظ لهذه الوظائف: إنها في block impl ولكن لا تحتوي على parameter &self.

هذه الوظائف لا تعتمد على المثال، ولكن لاستخدامها يجب أن يتم إعلانها في impl الذي يتم فيه كتابتها.

وظيفة String::from التي نستخدمها دائمًا هي "وظيفة مرتبطة".

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}
impl Rectangle {
    fn create(width: u32, height: u32) -> Rectangle {
        Rectangle { width, height }
    }
}
fn main() {
    let rect = Rectangle::create(30, 50);
    println!("{:?}", rect);
}

نتيجة التنفيذ:

Rectangle { width: 30, height: 50 }

نصيحة:يمكن كتابة block impl بنية المباني عدة مرات، مما يعادل تجميع محتوياتها!

بنية وحدة

يمكن للبنية أن تكون مجرد رمز دون الحاجة إلى أي أعضاء:

struct UnitStruct;

نسمي هذه المباني بدون جسم بنية وحدة (Unit Struct).