强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

03 - 基本语法

第 3 章:基本语法

本章涵盖 Vala 语言的基本构建块:变量、类型、函数和命名空间。


3.1 程序入口:main 函数

每个 Vala 程序必须有一个 main 函数作为入口点:

// 最简单的 Vala 程序
void main () {
    print ("Hello, World!\n");
}

3.1.1 带命令行参数的 main

// 使用字符串数组参数
void main (string[] args) {
    print ("程序名: %s\n", args[0]);
    print ("参数数量: %d\n", args.length - 1);

    for (int i = 1; i < args.length; i++) {
        print ("参数 %d: %s\n", i, args[i]);
    }
}
valac args_demo.vala -o args_demo
./args_demo foo bar baz
# 输出:
# 程序名: ./args_demo
# 参数数量: 3
# 参数 1: foo
# 参数 2: bar
# 参数 3: baz

3.1.2 带返回值的 main

int main (string[] args) {
    if (args.length < 2) {
        printerr ("用法: %s <名字>\n", args[0]);
        return 1;  // 非零返回值表示错误
    }

    print ("你好, %s!\n", args[1]);
    return 0;  // 成功
}

💡 print() 输出到 stdout,printerr() 输出到 stderr。


3.2 变量

3.2.1 变量声明

Vala 使用类型推断(var)或显式类型来声明变量:

void main () {
    // 显式类型声明
    int age = 30;
    string name = "张三";
    double salary = 15000.50;
    bool active = true;

    // 类型推断(var 关键字)
    var city = "北京";           // 推断为 string
    var count = 42;              // 推断为 int
    var pi = 3.14159;            // 推断为 double
    var done = false;            // 推断为 bool

    // 打印
    print ("姓名: %s, 年龄: %d\n", name, age);
    print ("城市: %s, 工资: %.2f\n", city, salary);
    print ("活跃: %s\n", active.to_string ());
}

3.2.2 变量命名规则

// ✅ 合法的变量名
int count = 0;
int user_age = 25;
int itemCount = 10;
int _internal = 42;
string 名字 = "中文变量名也可以";  // Vala 支持 Unicode 标识符

// ❌ 非法的变量名
// int 2count = 0;      // 不能以数字开头
// int my-var = 0;      // 不能包含连字符
// int class = 0;       // 不能使用关键字

3.2.3 常量

// 模块级常量
const int MAX_SIZE = 100;
const string APP_NAME = "MyApp";
const double PI = 3.14159265358979;

void main () {
    // const 常量必须在编译期确定值
    print ("最大大小: %d\n", MAX_SIZE);
    print ("应用名: %s\n", APP_NAME);
    print ("圆周率: %.10f\n", PI);

    // 局部常量
    const string GREETING = "你好";
    print ("%s\n", GREETING);
}

3.2.4 可空类型(Nullable Types)

void main () {
    // 非空字符串(默认)
    string name = "Vala";

    // 可空字符串(加 ? 后缀)
    string? nickname = null;

    // 检查 null
    if (nickname != null) {
        print ("昵称: %s\n", nickname);
    } else {
        print ("没有昵称\n");
    }

    // 可空类型用于可能失败的操作
    int? result = parse_int ("42");
    if (result != null) {
        print ("解析结果: %d\n", result);
    }

    int? bad = parse_int ("abc");
    if (bad == null) {
        print ("解析失败\n");
    }
}

int? parse_int (string str) {
    // 尝试将字符串转为整数
    int val = int.parse (str);
    if (val == 0 && str != "0") {
        return null;
    }
    return val;
}

⚠️ 重要:Vala 中,string 默认是可空的(因为底层是 C 指针),但 int 等值类型默认是非空的。使用 int? 表示可空的整数。


3.3 基本数据类型

3.3.1 类型总览

Vala 类型C 类型GLib 类型大小范围
bool_Boolgboolean1Btrue / false
int8int8_tgint81B-128 ~ 127
uint8 / ucharuint8_tguint81B0 ~ 255
int16int16_tgint162B-32768 ~ 32767
uint16uint16_tguint162B0 ~ 65535
int / int32intgint4B-2³¹ ~ 2³¹-1
uint / uint32unsigned intguint4B0 ~ 2³²-1
int64int64_tgint648B-2⁶³ ~ 2⁶³-1
uint64uint64_tguint648B0 ~ 2⁶⁴-1
floatfloatgfloat4B±3.4E38
doubledoublegdouble8B±1.7E308
charchargchar1BASCII 字符
unichargunichargunichar4BUnicode 码点
stringchar*gchar*指针UTF-8 字符串

3.3.2 整数类型

void main () {
    // 基本整数
    int a = 42;
    int64 big = 9223372036854775807;  // int64 最大值
    uint positive = 100;

    // 不同进制
    int decimal = 255;        // 十进制
    int hex = 0xFF;           // 十六进制
    int octal = 0o377;        // 八进制
    int binary = 0b11111111;  // 二进制

    print ("十进制: %d\n", decimal);
    print ("十六进制: 0x%x\n", hex);
    print ("八进制: 0o%o\n", octal);
    print ("二进制: 0b11111111\n");

    // 数字分隔符(Vala 0.52+)
    int million = 1_000_000;
    int phone = 138_0000_0000;
    print ("百万: %d\n", million);

    // 类型转换
    int x = 42;
    double y = (double) x;  // 显式转换
    int z = (int) 3.14;     // 截断
    print ("y=%.1f, z=%d\n", y, z);  // y=42.0, z=3
}

3.3.3 浮点类型

void main () {
    float f = 3.14f;       // float 需要 f 后缀
    double d = 3.14159;    // double 是默认浮点类型
    double scientific = 1.5e10;

    print ("float: %f\n", f);
    print ("double: %.5f\n", d);
    print ("科学计数法: %e\n", scientific);

    // 特殊值
    double inf = double.INFINITY;
    double nan = double.NAN;
    print ("无穷大: %f\n", inf);
    print ("NaN: %f\n", nan);
    print ("是否 NaN: %s\n", nan.is_nan ().to_string ());
}

3.3.4 布尔类型

void main () {
    bool flag = true;
    bool empty = false;

    // 布尔运算
    bool a = true && false;   // false
    bool b = true || false;   // true
    bool c = !true;           // false

    // 在 Vala 中,只有 true 和 false 是合法的布尔值
    // 不像 C,0 和 1 不能隐式转换为 bool
    print ("a=%s, b=%s, c=%s\n",
           a.to_string (),
           b.to_string (),
           c.to_string ());

    // 三元运算符
    string status = flag ? "启用" : "禁用";
    print ("状态: %s\n", status);
}

3.3.5 字符和字符串

void main () {
    // char —— 单个 ASCII 字符
    char letter = 'A';
    char digit = '0';
    print ("字符: %c, %c\n", letter, digit);

    // string —— UTF-8 字符串
    string greeting = "你好,世界!";
    string path = "/home/user";

    // 字符串长度(UTF-8 字符数,不是字节数)
    print ("\"%s\" 的长度: %d\n", greeting, greeting.length);

    // unichar —— Unicode 码点
    unichar heart = '♥';
    print ("心形: %c (U+%04X)\n", heart, heart);

    // 字符串拼接
    string first = "张";
    string last = "三";
    string full = first + last;  // 用 + 拼接
    print ("全名: %s\n", full);

    // 原始字符串(raw string)
    string raw = """这是一段
跨越多行的
原始字符串""";
    print ("%s\n", raw);

    // 字符串模板(Vala 0.52+ 不支持,使用 printf 风格)
    string name = "Vala";
    int ver = 0;
    // 使用 string.join 或 printf 风格
    string info = "语言: %s,版本: %d".printf (name, ver);
    print ("%s\n", info);
}

3.3.6 字符串常用方法

void main () {
    string s = "Hello, Vala World!";

    // 查找
    print ("包含 'Vala': %s\n", s.contains ("Vala").to_string ());
    print ("以 'Hello' 开头: %s\n", s.has_prefix ("Hello").to_string ());
    print ("以 '!' 结尾: %s\n", s.has_suffix ("!").to_string ());
    print ("'Vala' 的位置: %d\n", s.index_of ("Vala"));

    // 转换
    print ("大写: %s\n", s.up ());
    print ("小写: %s\n", s.down ());
    print ("去空格: %s\n", "  hello  ".strip ());

    // 截取
    print ("前5个字符: %s\n", s.substring (0, 5));
    print ("从第7个开始: %s\n", s.substring (7));

    // 替换
    print ("替换: %s\n", s.replace ("Vala", "Rust"));

    // 分割
    string csv = "a,b,c,d";
    string[] parts = csv.split (",");
    foreach (string part in parts) {
        print ("  部分: %s\n", part);
    }

    // 连接
    string[] words = {"Hello", "Vala", "World"};
    string joined = string.joinv (" ", words);
    print ("连接: %s\n", joined);

    // 格式化
    string formatted = "%s 有 %d 个字符".printf (s, s.length);
    print ("%s\n", formatted);
}

3.4 格式化输出

Vala 使用 GLib 的 printf 风格格式化:

格式符说明示例
%d整数"%d".printf(42)"42"
%s字符串"%s".printf("hi")"hi"
%f浮点数"%f".printf(3.14)"3.140000"
%.2f保留2位小数"%.2f".printf(3.14)"3.14"
%x十六进制(小写)"%x".printf(255)"ff"
%X十六进制(大写)"%X".printf(255)"FF"
%o八进制"%o".printf(8)"10"
%c字符"%c".printf('A')"A"
%p指针地址"%p".printf(ptr)
%ldlong int"%ld".printf(big)
%%字面量 %"100%%""100%"
void main () {
    string name = "Vala";
    int year = 2006;
    double ver = 0.58;

    // print 直接输出
    print ("%s 诞生于 %d 年,当前版本 %.1f\n", name, year, ver);

    // 格式化为字符串
    string info = "%s v%.1f (%d)".printf (name, ver, year);
    print ("%s\n", info);

    // 对齐和填充
    print ("|%-10s|%10d|%10.2f|\n", name, year, ver);
    // 输出:
    // |Vala      |      2006|      0.58|
}

3.5 运算符

3.5.1 算术运算符

void main () {
    int a = 10, b = 3;

    print ("加: %d + %d = %d\n", a, b, a + b);     // 13
    print ("减: %d - %d = %d\n", a, b, a - b);     // 7
    print ("乘: %d * %d = %d\n", a, b, a * b);     // 30
    print ("除: %d / %d = %d\n", a, b, a / b);     // 3(整数除法)
    print ("模: %d %% %d = %d\n", a, b, a % b);    // 1

    // 注意:整数除法会截断
    print ("10 / 3 = %d\n", 10 / 3);    // 3
    print ("10.0 / 3 = %.2f\n", 10.0 / 3);  // 3.33

    // 自增自减
    int c = 5;
    c++;     // c = 6
    c--;     // c = 5
    ++c;     // c = 6
    --c;     // c = 5
    print ("c = %d\n", c);

    // 复合赋值
    c += 10;  // c = 15
    c -= 3;   // c = 12
    c *= 2;   // c = 24
    c /= 4;   // c = 6
    c %= 5;   // c = 1
    print ("c = %d\n", c);
}

3.5.2 比较运算符

void main () {
    int x = 5, y = 10;

    print ("%d == %d: %s\n", x, y, (x == y).to_string ());  // false
    print ("%d != %d: %s\n", x, y, (x != y).to_string ());  // true
    print ("%d < %d: %s\n",  x, y, (x < y).to_string ());   // true
    print ("%d > %d: %s\n",  x, y, (x > y).to_string ());   // false
    print ("%d <= %d: %s\n", x, y, (x <= y).to_string ());   // true
    print ("%d >= %d: %s\n", x, y, (x >= y).to_string ());   // false
}

3.5.3 位运算符

void main () {
    uint a = 0b1100;  // 12
    uint b = 0b1010;  // 10

    print ("AND:  0x%X\n", a & b);   // 0x8 (0b1000)
    print ("OR:   0x%X\n", a | b);   // 0xE (0b1110)
    print ("XOR:  0x%X\n", a ^ b);   // 0x6 (0b0110)
    print ("NOT:  0x%X\n", ~a);      // 0xFFFFFFF3
    print ("左移: 0x%X\n", a << 1);  // 0x18 (0b11000)
    print ("右移: 0x%X\n", a >> 1);  // 0x6  (0b0110)
}

3.6 控制流

3.6.1 条件语句

void main () {
    int score = 85;

    // if-else if-else
    if (score >= 90) {
        print ("优秀\n");
    } else if (score >= 80) {
        print ("良好\n");
    } else if (score >= 60) {
        print ("及格\n");
    } else {
        print ("不及格\n");
    }

    // switch 语句
    string grade;
    switch (score / 10) {
        case 10:
        case 9:
            grade = "A";
            break;
        case 8:
            grade = "B";
            break;
        case 7:
            grade = "C";
            break;
        case 6:
            grade = "D";
            break;
        default:
            grade = "F";
            break;
    }
    print ("等级: %s\n", grade);
}

3.6.2 循环语句

void main () {
    // for 循环
    print ("--- for 循环 ---\n");
    for (int i = 0; i < 5; i++) {
        print ("  i = %d\n", i);
    }

    // while 循环
    print ("--- while 循环 ---\n");
    int count = 0;
    while (count < 3) {
        print ("  count = %d\n", count);
        count++;
    }

    // do-while 循环
    print ("--- do-while 循环 ---\n");
    int n = 0;
    do {
        print ("  n = %d\n", n);
        n++;
    } while (n < 3);

    // break 和 continue
    print ("--- break/continue ---\n");
    for (int i = 0; i < 10; i++) {
        if (i == 3) continue;  // 跳过 3
        if (i == 7) break;     // 到 7 停止
        print ("  i = %d\n", i);
    }

    // 循环标签(用于嵌套循环的 break/continue)
    print ("--- 循环标签 ---\n");
    outer:
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            if (i == 1 && j == 1) {
                break outer;  // 跳出外层循环
            }
            print ("  (%d, %d)\n", i, j);
        }
    }
}

3.6.3 foreach 循环

void main () {
    // 遍历数组
    string[] fruits = {"苹果", "香蕉", "橙子", "葡萄"};
    print ("--- 水果列表 ---\n");
    foreach (string fruit in fruits) {
        print ("  %s\n", fruit);
    }

    // 遍历带索引
    print ("--- 带索引 ---\n");
    for (int i = 0; i < fruits.length; i++) {
        print ("  %d: %s\n", i, fruits[i]);
    }

    // 遍历 GLib.List
    var list = new GLib.List<int> ();
    list.append (10);
    list.append (20);
    list.append (30);

    print ("--- GLib.List ---\n");
    foreach (int item in list) {
        print ("  %d\n", item);
    }

    // 遍历 GLib.Array
    var array = new GLib.Array<string> ();
    array.append_val ("hello");
    array.append_val ("world");

    print ("--- GLib.Array ---\n");
    foreach (string item in array.data) {
        print ("  %s\n", item);
    }
}

3.7 数组

3.7.1 基本数组

void main () {
    // 固定长度数组
    int[5] fixed = {1, 2, 3, 4, 5};

    // 动态数组
    int[] dynamic = {10, 20, 30};

    // 空数组
    string[] names = new string[0];

    // 添加元素
    names += "Alice";
    names += "Bob";
    names += "Charlie";

    print ("数组长度: %d\n", names.length);
    foreach (string name in names) {
        print ("  %s\n", name);
    }

    // 数组切片
    int[] nums = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    int[] slice = nums[2:5];  // [2, 3, 4]
    print ("切片: ");
    foreach (int s in slice) {
        print ("%d ", s);
    }
    print ("\n");

    // 多维数组
    int[,] matrix = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };
    print ("矩阵[1][2] = %d\n", matrix[1, 2]);  // 6
}

3.7.2 数组方法

void main () {
    // 使用 GLib 的数组工具
    int[] data = {5, 3, 8, 1, 9, 2, 7};

    // 排序
    // data.sort ((a, b) => a - b);  // 升序
    // 使用 qsort_with_data
    GLib.qsort_with_data<int> (data, sizeof (int), (a, b) => {
        return a - b;
    });
    print ("排序后: ");
    foreach (int d in data) {
        print ("%d ", d);
    }
    print ("\n");

    // 移动语义
    int[] a = {1, 2, 3};
    int[] b = a;  // 移动语义!a 现在为空
    print ("a 长度: %d\n", a.length);  // 0
    print ("b 长度: %d\n", b.length);  // 3

    // 如果要复制,使用 dup
    int[] c = {4, 5, 6};
    int[] d = c;  // 注意:这是移动,不是复制
    // 要复制需要手动操作
}

⚠️ 重要:Vala 中数组赋值是移动语义,不是复制!赋值后原变量将变为空。


3.8 函数

3.8.1 函数定义

// 基本函数
void greet (string name) {
    print ("你好, %s!\n", name);
}

// 带返回值
int add (int a, int b) {
    return a + b;
}

// 带默认参数
void print_info (string name, int age = 0, string city = "未知") {
    print ("%s, %d岁, %s\n", name, age, city);
}

// 可变参数
void log (string format, ...) {
    var args = va_list ();
    // 注意:Vala 的可变参数处理与 C 类似
    print (format + "\n");
}

void main () {
    greet ("世界");

    int sum = add (3, 5);
    print ("3 + 5 = %d\n", sum);

    print_info ("张三", 30, "北京");
    print_info ("李四", 25);         // 使用默认城市
    print_info ("王五");             // 使用默认年龄和城市
}

3.8.2 命名参数

void create_user (string name, int age, string email) {
    print ("创建用户: %s, %d岁, %s\n", name, age, email);
}

void main () {
    // 使用命名参数(顺序可以不同)
    create_user (name: "张三", age: 30, email: "[email protected]");
    create_user (email: "[email protected]", name: "李四", age: 25);

    // 混合使用位置参数和命名参数
    create_user ("王五", age: 28, email: "[email protected]");
}

3.8.3 返回多个值(out 参数)

// 使用 out 参数返回多个值
void divide (int a, int b, out int quotient, out int remainder) {
    quotient = a / b;
    remainder = a % b;
}

// 使用 out 参数进行可能失败的操作
bool try_parse (string str, out int result) {
    if (str.length == 0) {
        result = 0;
        return false;
    }

    // 简单的数字解析
    result = 0;
    for (int i = 0; i < str.length; i++) {
        char c = str[i];
        if (c < '0' || c > '9') {
            result = 0;
            return false;
        }
        result = result * 10 + (c - '0');
    }
    return true;
}

void main () {
    // 使用 out 参数
    int q, r;
    divide (17, 5, out q, out r);
    print ("17 / 5 = %d 余 %d\n", q, r);

    // 使用可能失败的操作
    int val;
    if (try_parse ("123", out val)) {
        print ("解析成功: %d\n", val);
    } else {
        print ("解析失败\n");
    }

    if (try_parse ("abc", out val)) {
        print ("解析成功: %d\n", val);
    } else {
        print ("解析失败\n");
    }
}

3.8.4 Lambda 表达式

void main () {
    // 简单的 lambda
    var greet = (name) => {
        print ("你好, %s!\n", name);
    };
    greet ("Vala");

    // 作为回调
    string[] names = {"Charlie", "Alice", "Bob"};

    // 使用 GLib 的排序函数
    // 注意:Vala 的数组排序使用 qsort_with_data
    var list = new GLib.List<string> ();
    foreach (string name in names) {
        list.append (name);
    }

    list.sort ((a, b) => {
        return strcmp (a, b);
    });

    print ("排序后: ");
    foreach (string name in list) {
        print ("%s ", name);
    }
    print ("\n");

    // 带类型的 lambda
    var add = (int a, int b) => {
        return a + b;
    };
    print ("2 + 3 = %d\n", add (2, 3));
}

3.8.5 委托(Delegate)

// 定义委托类型
delegate int MathFunc (int a, int b);

// 使用委托的函数
int apply (int a, int b, MathFunc func) {
    return func (a, b);
}

int multiply (int a, int b) { return a * b; }
int subtract (int a, int b) { return a - b; }

void main () {
    // 使用函数引用
    print ("3 * 4 = %d\n", apply (3, 4, multiply));
    print ("10 - 3 = %d\n", apply (10, 3, subtract));

    // 使用 lambda
    print ("2 + 5 = %d\n", apply (2, 5, (a, b) => a + b));
}

3.9 命名空间

3.9.1 基本命名空间

// 定义命名空间
namespace MathUtils {
    public int factorial (int n) {
        if (n <= 1) return 1;
        return n * factorial (n - 1);
    }

    public double circle_area (double radius) {
        return 3.14159 * radius * radius;
    }

    // 嵌套命名空间
    namespace Geometry {
        public struct Point {
            public double x;
            public double y;
        }

        public double distance (Point a, Point b) {
            double dx = a.x - b.x;
            double dy = a.y - b.y;
            return Math.sqrt (dx * dx + dy * dy);
        }
    }
}

void main () {
    // 使用命名空间中的函数
    print ("5! = %d\n", MathUtils.factorial (5));
    print ("圆面积(r=3): %.2f\n", MathUtils.circle_area (3.0));

    // 使用嵌套命名空间
    MathUtils.Geometry.Point p1 = {0, 0};
    MathUtils.Geometry.Point p2 = {3, 4};
    print ("两点距离: %.2f\n", MathUtils.Geometry.distance (p1, p2));
}

3.9.2 using 指令

using MathUtils;
using MathUtils.Geometry;

void main () {
    // 使用 using 后可以省略命名空间前缀
    print ("5! = %d\n", factorial (5));
    print ("圆面积(r=3): %.2f\n", circle_area (3.0));

    Point p1 = {0, 0};
    Point p2 = {3, 4};
    print ("两点距离: %.2f\n", distance (p1, p2));
}

💡 命名空间不生成任何 C 代码,它仅在编译期用于符号解析。Vala 的命名空间纯粹是编译器层面的概念。


3.10 枚举

// 基本枚举
enum Color {
    RED,
    GREEN,
    BLUE;

    // 枚举方法
    public string to_string () {
        switch (this) {
            case RED:   return "红色";
            case GREEN: return "绿色";
            case BLUE:  return "蓝色";
            default:    return "未知";
        }
    }
}

// 带值的枚举
enum HttpStatus {
    OK = 200,
    NOT_FOUND = 404,
    INTERNAL_ERROR = 500;
}

// 标志枚举(位域)
[Flags]
enum Permissions {
    NONE = 0,
    READ = 1,
    WRITE = 2,
    EXECUTE = 4;
}

void main () {
    Color c = Color.RED;
    print ("颜色: %s\n", c.to_string ());

    HttpStatus status = HttpStatus.NOT_FOUND;
    print ("HTTP 状态码: %d\n", status);

    // 标志枚举的使用
    Permissions perms = Permissions.READ | Permissions.WRITE;
    print ("权限: %s\n", perms.to_string ());
    print ("有读权限: %s\n", (perms & Permissions.READ).to_string ());
    print ("有执行权限: %s\n", (perms & Permissions.EXECUTE).to_string ());
}

3.11 结构体

// 基本结构体
struct Point {
    public double x;
    public double y;

    // 结构体方法
    public double distance_to (Point other) {
        double dx = x - other.x;
        double dy = y - other.y;
        return Math.sqrt (dx * dx + dy * dy);
    }
}

// 带构造器的结构体
struct Size {
    public int width;
    public int height;

    // 结构体没有构造器语法,使用静态工厂方法
    public static Size create (int w, int h) {
        Size s = Size ();
        s.width = w;
        s.height = h;
        return s;
    }

    public int area () {
        return width * height;
    }
}

void main () {
    Point p1 = {10.0, 20.0};
    Point p2 = {13.0, 24.0};
    print ("距离: %.2f\n", p1.distance_to (p2));

    Size s = Size.create (100, 200);
    print ("尺寸: %dx%d, 面积: %d\n", s.width, s.height, s.area ());
}

3.12 错误处理

// 定义错误域
errordomain FileError {
    NOT_FOUND,
    PERMISSION_DENIED,
    IO_ERROR
}

// 抛出错误的函数
string read_file (string path) throws FileError {
    if (path == "") {
        throw new FileError.NOT_FOUND ("文件路径不能为空");
    }

    try {
        // 使用 GLib 的文件操作
        string content;
        size_t length;
        GLib.FileUtils.get_contents (path, out content, out length);
        return content;
    } catch (GLib.Error e) {
        throw new FileError.IO_ERROR ("读取文件失败: " + e.message);
    }
}

void main () {
    try {
        string content = read_file ("/etc/hostname");
        print ("文件内容: %s\n", content);
    } catch (FileError e) {
        printerr ("错误: %s\n", e.message);
    }

    // 处理不存在的文件
    try {
        string content = read_file ("/nonexistent");
        print ("文件内容: %s\n", content);
    } catch (FileError e) {
        printerr ("错误: %s\n", e.message);
    }
}

3.13 注意事项

⚠️ 初学者常见陷阱

  1. 数组是移动语义int[] b = a; 会使 a 变为空
  2. 字符串不可变:Vala 字符串是不可变的,修改会创建新字符串
  3. 值类型 vs 引用类型struct 是值类型,class 是引用类型
  4. null 安全:引用类型默认可以为 null,注意空指针检查
  5. 分号必须:每条语句后必须有分号(不像 Go)
  6. 类型推断var 推断类型,但变量类型一旦确定不能改变

3.14 扩展阅读

资源链接
Vala 语言规范https://wiki.gnome.org/Projects/Vala/LanguageSpecification
Vala 类型系统https://wiki.gnome.org/Projects/Vala/TypeSystem
Vala 教程https://wiki.gnome.org/Projects/Vala/Tutorial
Valadoc API 文档https://valadoc.org/
GLib 文档https://docs.gtk.org/glib/

3.15 总结

要点说明
入口函数void main()int main(string[] args)
类型推断使用 var 关键字
可空类型值类型加 ? 后缀
数组赋值移动语义,非复制
错误处理throws + try/catch + errordomain
命名空间编译期概念,不生成 C 代码
格式化printf 风格,使用 %d%s

下一章我们将学习 Vala 的面向对象编程。→ 第 4 章:面向对象编程