Menu
CoddyTech

פונקציות גנריות

חלק מהיחידה תכנות מונחה עצמים במסלול ה-Rust של Coddy. שיעור 27 מתוך 61.

Generics אינם מוגבלים למבנים. אפשר גם לכתוב פונקציות עצמאיות שעובדות עם כל סוג. זה שימושי כשצריך פונקציית עזר שלא שייכת למבנה מסוים, אבל עדיין צריכה להיות גמישה.

התחביר דומה לזה שראית עם מבנים. הכריזו על הפרמטר הגנרי בתוך סוגריים זוויתיים אחרי שם הפונקציה, ואז השתמשו בו בפרמטרים ובטיפוס ההחזרה:

fn identity<T>(value: T) -> T {
    value
}

הפונקציה identity מקבלת ערך מכל סוג שהוא ומחזירה אותו ללא שינוי. <T> מצהיר על הטיפוס הגנרי, value: T מקבל אותו כפרמטר, ו־-> T מציין את טיפוס ההחזרה. כשקוראים לפונקציה, Rust מסיק את הטיפוס הקונקרטי:

let num = identity(42);        // T הוא i32
let text = identity("hello");  // T הוא &str

אפשר גם להשתמש בכמה פרמטרים גנריים בפונקציות, בדיוק כמו במבנים:

fn make_pair<T, U>(first: T, second: U) -> (T, U) {
    (first, second)
}

let pair = make_pair(10, "ten");  // מחזירה (i32, &str)

פונקציות גנריות מאפשרות לך לכתוב לוגיקה רב־פעמית פעם אחת ולהחיל אותה על טיפוסים רבים, תוך צמצום שכפול הקוד ושמירה על בטיחות הטיפוסים.

challenge icon

אתגר

קל

בואו נבנה מודול עזר עם פונקציות גנריות שיכולות לעבוד עם כל טיפוס! תיצרו פונקציות עצמאיות שמדגימות איך גנריות הופכות את הקוד שלכם לגמיש ולשימוש חוזר, בלי לקשור אותו למבנה נתונים מסוים.

תארגנו את הקוד שלכם בשני קבצים:

  • utils.rs: צרו אוסף של פונקציות עזר גנריות ציבוריות:
    • wrap_in_pair<T>: מקבלת ערך יחיד ומחזירה זוג (tuple) שמכיל את אותו ערך פעמיים: (value, value). לשם כך הטיפוס חייב לממש את Clone, לכן השתמשו ב-<T: Clone>
    • swap<T, U>: מקבלת שני ערכים שעשויים להיות מטיפוסים שונים ומחזירה אותם בסדר הפוך כזוג (U, T)
  • main.rs: ייבאו את מודול העזר שלכם והדגימו את הפונקציות הגנריות האלה בעבודה עם טיפוסים שונים. הראו איך אותן הגדרות של פונקציות מטפלות במספרים שלמים, במספרים ממשיים ובמחרוזות בצורה חלקה.

בקובץ הראשי שלכם, הדגימו את פונקציות העזר שלכם כך:

  1. השתמשו ב-wrap_in_pair עם מספר שלם (הקלט הראשון, שמנותח כ-i32) והדפיסו את שני האיברים
  2. השתמשו ב-wrap_in_pair עם מחרוזת (הקלט השני) והדפיסו את שני האיברים
  3. השתמשו ב-swap עם מספר שלם (הקלט השלישי, שמנותח כ-i32) ומחרוזת (הקלט הרביעי), ואז הדפיסו את התוצאה לאחר החלפת הסדר

הפלט שלכם צריך להיות בפורמט הזה:

Pair of ints: ({value}, {value})
Pair of strings: ({value}, {value})
Swapped: ({string}, {int})

לדוגמה, עבור הקלטים 5, hello, 42 ו-world:

Pair of ints: (5, 5)
Pair of strings: (hello, hello)
Swapped: (world, 42)

שימו לב איך wrap_in_pair פועלת באופן זהה גם עם מספרים שלמים וגם עם מחרוזות, ו-swap מטפלת בשני טיפוסים שונים לחלוטין: זו הגמישות של פונקציות גנריות!

תקבלו ארבעה קלטים: מספר שלם, מחרוזת, מספר שלם נוסף ומחרוזת נוספת.

נסו בעצמכם

mod utils;

use std::io;

fn main() {
    // קראו את ארבעת הקלטים
    let mut input1 = String::new();
    io::stdin().read_line(&mut input1).expect("Failed to read line");
    let num1: i32 = input1.trim().parse().expect("Invalid integer");

    let mut input2 = String::new();
    io::stdin().read_line(&mut input2).expect("Failed to read line");
    let str1 = input2.trim().to_string();

    let mut input3 = String::new();
    io::stdin().read_line(&mut input3).expect("Failed to read line");
    let num2: i32 = input3.trim().parse().expect("Invalid integer");

    let mut input4 = String::new();
    io::stdin().read_line(&mut input4).expect("Failed to read line");
    let str2 = input4.trim().to_string();

    // TODO: השתמשו ב-wrap_in_pair עם num1 והדפיסו את התוצאה
    // פורמט: Pair of ints: ({value}, {value})

    // TODO: השתמשו ב-wrap_in_pair עם str1 והדפיסו את התוצאה
    // פורמט: Pair of strings: ({value}, {value})

    // TODO: השתמשו ב-swap עם num2 ו-str2, ואז הדפיסו את התוצאה לאחר ההחלפה
    // פורמט: Swapped: ({string}, {int})
}
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר Rust אונליין