הצהרה על Shape
shape הוא טיפוס רשומה עם שדות בעלי שם וטיפוס:
shape Point {
x: i32,
y: i32,
}
החלקים:
shapeפותח את ההצהרה.Pointהוא שם הטיפוס.- הסוגריים המסולסלים עוטפים רשימה של שדות, כל אחד בצורה
name: Type.
ההצהרה הזו מוסיפה לתחום הנוכחי טיפוס חדש בשם Point. בכל מקום שבו אפשר להשתמש בטיפוס מובנה כמו i32, אפשר עכשיו להשתמש גם ב-Point.
בניית ערך Shape
יוצרים מופע עם ביטוי ליטרל של struct:
let point = Point { x: 40, y: 2 }
הליטרל מציין את ה-shape ומשייך ערך לכל שדה. כל שדה חייב להופיע: Zero לא ממלאת בשקט שדות חסרים באפס או ב-null. אם שכחתם אחד, הקומפיילר אומר לכם:
{
"code": "FLD002",
"message": "missing field: y",
"line": 4
}
(קוד השגיאה המדויק עשוי להשתנות; העיקרון של "אין ברירות מחדל מרומזות" הוא הקבוע.)
אפשר לציין את הטיפוס במפורש אם רוצים שהוא יהיה גלוי:
let point: Point = Point { x: 40, y: 2 }
קריאת שדות
גישה לשדות משתמשת בתחביר נקודה:
let point = Point { x: 40, y: 2 }
let xVal = point.x
let yVal = point.y
point.x קורא את השדה x. אין מתודה get_x(): שדות הם נתונים פשוטים.
דוגמה מלאה
זו דוגמת point.0 הקנונית מהמאגר של השפה. לחצו על Run כדי לנסות:
נעבור עליה מההתחלה:
- מצהירים על shape בשם
Pointעם שני שדותi32. - מגדירים פונקציה
sumשמקבלתPointומחזירה את סכום השדות שלו. - ב-
main, בוניםPoint, קוראים ל-sumומשווים את התוצאה.
שלושה צעדים, שלושה רעיונות שקשורים ל-shapes (הצהרה, בנייה, גישה), ואפקט אחד: ה-check world.out.write(...) בסוף. שימו לב ש-sum לא נוגעת ב-World. זו פונקציה טהורה על נתונים, והחתימה הופכת את זה לברור.
Shapes עם שדות מקוננים
shape יכול להחזיק ערכים מכל טיפוס, כולל shapes אחרים:
shape Range {
start: i32,
end: i32,
}
shape Segment {
label: String,
range: Range,
}
let seg = Segment {
label: "warmup",
range: Range { start: 0, end: 10 },
}
גישה לשדות משתרשרת כמו שהייתם מצפים:
let len = seg.range.end - seg.range.start
Shapes גנריים
כשהשדות של shape צריכים להיות פולימורפיים בטיפוס, מצהירים על פרמטרי טיפוס בסוגריים משולשים:
shape Pair<T, U> {
left: T,
right: U,
}
המופעים קובעים את הפרמטרים לטיפוסים קונקרטיים:
let intBytePair: Pair<i32, u8> = Pair { left: 40, right: 2_u8 }
let words: Pair<String, String> = Pair { left: "hello", right: "world" }
כינוי טיפוס יכול לקצר פרמטריזציה נפוצה:
type BytePair = Pair<u8, u8>
let bytes: BytePair = Pair { left: 1_u8, right: 2_u8 }
Generics מכסה פרמטרי טיפוס לעומק, כולל בפונקציות ולא רק ב-shapes.
מה Shapes הם לא
כמה דברים שאולי הייתם מצפים להם מ-"struct" בשפה אחרת ו-shapes בכוונה לא כוללים:
- אין מתודות. הצהרת shape היא נתונים בלבד. ההתנהגות נמצאת בפונקציות חופשיות שמקבלות את ה-shape כפרמטר. זה משקף את אותה הפרדה בין נתונים לאפקטים שרואים בפונקציות.
- אין ירושה. Shapes לא מרחיבים shapes אחרים. אם רוצים מבנה משותף, הוציאו אותו לשדה משותף או בנו טיפוס סכום עם choice.
- אין בנאים או destructors מרומזים. הבנייה היא ביטוי הליטרל של ה-struct. הניקוי מפורש: כשהספרייה הסטנדרטית חושפת משאבים שצריך לשחרר, זה נעשה דרך ממשקים בסגנון יכולות ולא דרך RAII נסתר.
- אין שדות פרטיים. כל השדות של shape נגישים לקוד שיכול לראות את הטיפוס של ה-shape. הנראות היא ברמת הטיפוס, לא ברמת השדה.
הדפוס הוא: shapes הם טיפוסי רשומה פשוטים וצפויים, ואת כל השאר בונים מהם.
מתי להשתמש ב-Shape ומתי ב-Choice
מדריך מהיר:
- השתמשו ב-shape כשלערך יש את כל השדות האלה יחד. ל-
Pointיש תמיד גםxוגםy. - השתמשו ב-choice כשערך הוא אחת מתוך כמה חלופות.
Resultהוא אוokאוerr. - השתמשו ב-enum כשהחלופות לא נושאות נתונים נוספים: הן רק תוויות. ימות השבוע, מצבים פשוטים.
שלוש אבני הבניין האלה, shape (וגם), choice (או), enum (או בלי מטען), מכסות כמעט כל צורך במידול נתונים.
הבא בתור: Generics
ראיתם את Pair<T, U> מופיע בדרך אגב. המסמך הבא, generics, מסביר איך פרמטרי טיפוס עובדים גם ב-shapes וגם בפונקציות, כולל הדפוסים שמופיעים בכל הספרייה הסטנדרטית של Zero.
שאלות נפוצות
מה זה shape ב-Zero?
shape הוא טיפוס המכפלה של Zero בסגנון struct: רשומה בעלת שם עם שדות מטיפוסים מוגדרים. מצהירים עליו עם shape Name { field1: T1, field2: T2 }, בונים ערכים עם Name { field1: v1, field2: v2 } וקוראים שדות בתחביר נקודה (value.field1). Shapes הם אבן הבניין למידול נתונים מובנים.
איך יוצרים ערך shape?
משתמשים בביטוי ליטרל של struct שמציין את ה-shape ומשייך ערך לכל שדה: let point = Point { x: 40, y: 2 }. חייבים למלא כל שדה: Zero לא ממלאת בשקט ערכי ברירת מחדל לשדות חסרים. סדר השדות בליטרל לא חייב להתאים להצהרה.
במה shape שונה ממחלקה?
shape הוא נתונים פשוטים: יש לו שדות, אבל אין מתודות, אין ירושה ואין בנאים מרומזים. פונקציות שפועלות על shape מקבלות אותו כפרמטר במפורש. ההפרדה הזו שומרת על שפה קטנה ועל עלות צפויה לבנייה או להעתקה של shape, בלי vtables או destructors נסתרים.
האם shapes יכולים להיות גנריים ב-Zero?
כן. מצהירים על פרמטרי טיפוס בסוגריים משולשים: shape Pair<T, U> { left: T, right: U }. המופעים קובעים את הפרמטרים האלה: Pair<i32, u8>. Shapes גנריים מופיעים בכל הספרייה הסטנדרטית: Maybe<T>, Span<T> וכן הלאה הם כולם shapes גנריים או טיפוסי סכום שבנויים על אותו רעיון.
האם shapes מועתקים או מועברים כהפניה כשמעבירים אותם לפונקציות?
העברה לפי ערך היא המודל המנטלי שמוגדר כברירת מחדל ל-shapes: הפונקציה הנקראת רואה עותק לוגי משלה של הנתונים, לא הפניה לתוך הקישור של הקורא. מודל הזיכרון המדויק ב-Zero שלפני 1.0 עדיין מתפתח (תראו ref ו-mutref בדוגמאות של הספרייה הסטנדרטית לטיפוסי הפניה מפורשים). ברוב קוד האפליקציות, התייחסו לפרמטרים מסוג shape כקלטים לפי ערך.