- Zig ใช้ ไวยากรณ์แบบวงเล็บปีกกา คล้ายกับ Rust แต่ปรับปรุงด้วยความหมายของภาษาที่เรียบง่ายกว่าและตัวเลือกด้านไวยากรณ์ที่ประณีตกว่า
- ลิเทอรัลจำนวนเต็ม ทุกตัวเริ่มต้นเป็น
comptime_int และจะถูกแปลงแบบชัดเจนเมื่อมีการกำหนดค่า ส่วน ลิเทอรัลสตริง ใช้รูปแบบ raw string แบบย่อที่อิงกับ \\
- record literal ในรูปแบบ
.x = 1 ทำให้ค้นหาการเขียนค่าให้ฟิลด์ได้ง่าย และทุกชนิดข้อมูลถูกแสดงอย่างสม่ำเสมอด้วยรูปแบบ prefix
- ใช้
and·or เป็นคีย์เวิร์ดควบคุมการไหล และคำสั่ง if·loop สามารถละวงเล็บปีกกาได้ตามต้องการ โดยตัว formatter จะช่วยรับประกันความปลอดภัย
- โดยไม่มีเนมสเปซ Zig จัดการ ทุกอย่างเป็น expression เพื่อรวมไวยากรณ์ของชนิดข้อมูล·ค่า·แพตเทิร์นเข้าด้วยกัน และใช้งาน generic·record literal·ฟังก์ชันในตัว (
@import, @as เป็นต้น) ได้อย่างกระชับ
ภาพรวม
- Zig มีรูปลักษณ์คล้าย Rust แต่เลือกใช้โครงสร้างภาษาที่เรียบง่ายกว่า
- การออกแบบไวยากรณ์มุ่งเน้นที่ ความเป็นมิตรต่อ grep, ความสม่ำเสมอของไวยากรณ์, และ การลดสิ่งรบกวนทางสายตาที่ไม่จำเป็น
ลิเทอรัลจำนวนเต็ม
const an_integer = 92;
assert(@TypeOf(an_integer) == comptime_int);
const x: i32 = 92;
const y = @as(i32, 92);
- ลิเทอรัลจำนวนเต็มทั้งหมดมีชนิดเป็น
comptime_int
- เมื่อต้องกำหนดค่าให้ตัวแปร ต้องระบุชนิดอย่างชัดเจนหรือใช้
@as เพื่อแปลง
- รูปแบบ
var x = 92; ใช้งานไม่ได้ และต้องระบุชนิดอย่างชัดเจน
ลิเทอรัลสตริง
const raw =
\\Roses are red
\\ Violets are blue,
\\Sugar is sweet
\\ And so are you.
\\
;
- แต่ละบรรทัดเป็นโทเค็นแยกกัน จึงไม่มีปัญหาเรื่องการเยื้อง
- ไม่จำเป็นต้อง escape
\\ เอง
record literal
const p: Point = .{
.x = 1,
.y = 2,
};
- รูปแบบ
.x = 1 ช่วยแยกความต่างระหว่างการอ่าน/เขียนได้ดี
- สัญกรณ์
.{} แยกจาก block ได้ชัดเจน และแปลงเป็นชนิดผลลัพธ์โดยอัตโนมัติ
สัญกรณ์ชนิดข้อมูล
u32 // จำนวนเต็ม
[3]u32 // อาร์เรย์ความยาว 3
?[3]u32 // อาร์เรย์ที่เป็น null ได้
*const ?[3]u32 // พอยน์เตอร์ค่าคงที่
- ชนิดข้อมูลทั้งหมดใช้สัญกรณ์แบบ prefix
- การ dereference ใช้สัญกรณ์แบบ suffix (
ptr.*)
identifier
const @"a name with space" = 42;
- ใช้หลีกเลี่ยงการชนกับคีย์เวิร์ด หรือกำหนดชื่อพิเศษได้
การประกาศฟังก์ชัน
pub fn main() void {}
fn add(x: i32, y: i32) i32 {
return x + y;
}
- คีย์เวิร์ด
fn กับชื่อฟังก์ชันอยู่ติดกัน ทำให้ค้นหาได้ง่าย
- การระบุชนิดคืนค่าไม่ใช้
->
การประกาศตัวแปร
const mid = lo + @divFloor(hi - lo, 2);
var count: u32 = 0;
- ใช้
const และ var
- การระบุชนิดอยู่ในลำดับ
ชื่อ: ชนิด
การควบคุมการไหล: and/or
while (count > 0 and ascii.isWhitespace(buffer[count - 1])) {
count -= 1;
}
and, or เป็นคีย์เวิร์ดควบคุมการไหล
- การคำนวณแบบบิตใช้
&, |
คำสั่ง if
.direction = if (prng.boolean()) .ascending else .descending;
- วงเล็บจำเป็น แต่วงเล็บปีกกาเป็นทางเลือก
zig fmt ช่วยรับประกันการจัดรูปแบบที่ปลอดภัย
ลูป
for (0..10) |i| {
print("{d}\n", .{i});
} else @panic("loop safety counter exceeded");
- ทั้ง
for และ while รองรับส่วน else
- จัดวาง iterator และชื่อตัวแปรองค์ประกอบได้อย่างเข้าใจง่าย
เนมสเปซและการ resolve ชื่อ
const std = @import("std");
const ArrayList = std.ArrayList;
- ไม่อนุญาตให้ shadow ตัวแปร
- ไม่มีเนมสเปซและไม่มี glob import
ทุกอย่างคือ expression
const E = enum { a, b };
const e: if (true) E else void = .a;
- รวมไวยากรณ์ของชนิดข้อมูล·ค่า·แพตเทิร์นเข้าด้วยกัน
- สามารถวาง conditional expression ในตำแหน่งของชนิดข้อมูลได้
generic
fn ArrayListType(comptime T: type) type {
return struct {
fn init() void {}
};
}
var xs: ArrayListType(u32) = .init();
- generic แสดงด้วยไวยากรณ์แบบการเรียกฟังก์ชัน (
Type(T))
- อาร์กิวเมนต์ชนิดข้อมูลต้องระบุอย่างชัดเจนเสมอ
ฟังก์ชันในตัว
const foo = @import("./foo.zig");
const num = @as(i32, 92);
- เรียกใช้ความสามารถที่คอมไพเลอร์มีให้ด้วย prefix
@
@import แสดงพาธไฟล์อย่างชัดเจน
- อาร์กิวเมนต์ต้องเป็น string literal เท่านั้น
บทสรุป
- ไวยากรณ์ของ Zig เป็นตัวอย่างของการที่ ตัวเลือกเล็กๆ หลายอย่าง รวมกันจนกลายเป็น ภาษาที่อ่านง่าย
- เมื่อจำนวนฟีเจอร์ลดลง ไวยากรณ์ที่จำเป็นก็ลดลงด้วย และ โอกาสเกิดการชนกันระหว่างไวยากรณ์ก็ลดลง
- ยืม แนวคิดที่ดีจากภาษาเดิม มาใช้ แต่เมื่อจำเป็นก็กล้านำไวยากรณ์ใหม่มาใช้
ยังไม่มีความคิดเห็น