核心特性
原子类型
atomic T 关键字,自动原子指令,零运行时锁。
内存安全强制
所有 UB 必须被编译期证明为安全,失败即编译错误。
并发安全强制
零数据竞争,编译期证明安全。
AI友好
简洁明了的语法,易于AI理解和生成代码。
泛型语法
使用尖括号 Vec<T>,约束紧邻参数 <T: Ord>,多约束连接 <T: Ord + Clone + Default>。
extern struct 完全解放
C 兼容结构体获得 Uya 超能力:可以有方法、drop、实现接口,同时保持 100% C 兼容性。
核心创新
1. 泛型语法确定
// 泛型函数
fn max<T: Ord>(a: T, b: T) T { ... }
// 泛型结构体
struct Vec<T: Default> { ... }
// 泛型接口
interface Iterator<T> { ... }
// 使用尖括号 <T>,约束紧邻参数 <T: Ord>
2. extern struct 完全解放
interface IReadable {
fn read(self: &Self, buf: *byte, len: i32) !i32;
}
struct File : IReadable {
fd: i32
}
File {
// ✅ 可以有方法
fn read(self: &Self, buf: *byte, len: i32) !i32 {
defer { log_read(); }
// ...
}
// ✅ 可以有 drop
fn drop(self: &Self) void {
close(self.fd);
}
}
最酷的部分:同一个结构体,两面性:
- C 代码看到:纯数据,标准布局
- Uya 代码看到:完整对象,有方法、接口、RAII
- C 兼容性 100% 保持
- Uya 能力 100% 获得
字符串插值
支持 "a${x}" 和 "pi=${pi:.2f}" 两种形式,支持定点格式(%f)、科学计数法(%e、%E)等格式说明符。
const x: u32 = 255;
const pi: f64 = 3.1415926;
const msg: [i8: 64] = "hex=${x:#06x}, pi=${pi:.2f}\n";
移动语义
结构体赋值时转移所有权,避免不必要的拷贝。
安全指针算术
支持 ptr +/- offset,必须通过编译期证明安全。
模块系统
目录级模块、显式导出、路径导入,编译期解析。
简化 for 循环语法
支持 for obj |v| {}、for 0..10 |v| {}、for obj |&v| {} 等简化语法。
运算符简化
try 关键字用于溢出检查,+|/-|/*| 用于饱和运算,+%/-%/*% 用于包装运算。
设计哲学
核心思想:坚如磐石
Uya语言的设计哲学是坚如磐石(绝对可靠、不可动摇),将所有运行时不确定性转化为编译期的确定性结果:要么证明绝对安全,要么返回显式错误。
核心机制
- 程序员提供显式证明,编译器验证证明的正确性
- 编译器在编译期完成所有安全验证,证明超时则自动插入运行时检查
- 每个操作都有明确的数学证明,消除任何运行时不确定性
- 零运行时未定义行为,程序行为完全可预测
示例对比
// C/C++:程序员负责,编译器不检查
int arr[10];
int i = get_index(); // 可能越界
int x = arr[i]; // 运行时可能崩溃,编译器不警告
// Rust:借用检查器帮助,但需要所有权系统
let arr = [0; 10];
let i = get_index();
if i < arr.len() {
let x = arr[i]; // 借用检查器保证安全
} else {
// 需要处理越界情况
}
// Uya:程序员提供证明,编译器验证证明
const arr: [i32: 10] = [0: 10];
const i: i32 = get_index();
if i < 0 || i >= 10 {
return error.OutOfBounds; // 显式检查,返回预定义错误(可选)
}
// 或者使用运行时错误(无需预定义)
if i < 0 || i >= 10 {
return error.IndexOutOfBounds; // 运行时错误,无需预定义
}
const x: i32 = arr[i]; // 编译器证明 i >= 0 && i < 10,安全
责任转移的哲学
| 语言 | 安全责任 | 编译器角色 |
|---|---|---|
| C/C++ | 程序员负责安全 | 编译器不帮忙,只生成代码 |
| Rust | 编译器通过借用检查器帮忙 | 编译器主动检查所有权和生命周期 |
| Uya | 程序员必须提供证明,编译器验证证明 | 编译器验证数学证明,失败即编译错误 |
结果与收益
Uya的"坚如磐石"设计哲学带来以下不可动摇的收益:
- 绝对的安全性:通过数学证明彻底消除缓冲区溢出、空指针解引用等内存安全漏洞
- 完全的可预测性:程序行为在编译期完全确定,无任何运行时未定义行为
- 最优的性能:开发者编写显式安全检查,编译器在当前函数内验证其充分性,消除冗余的运行时安全检查,仅保留必要的错误处理逻辑
- 明确的错误处理:可能失败的操作返回错误联合类型
!T,强制显式错误处理 - 长期的可维护性:代码行为清晰可预测,减少调试和维护成本
性能保证
// Uya:开发者编写显式检查,编译器在当前函数内验证
fn safe_access(arr: [i32: 10], i: i32) !i32 {
if i < 0 || i >= 10 {
return error.OutOfBounds; // 显式错误处理
}
// 编译器证明:当执行到这里时,i >= 0 && i < 10 必然成立
return arr[i]; // 直接访问,无运行时边界检查
}
// 生成的代码:
// cmp rdi, 0 // 运行时错误检查
// jl .error_out_of_bounds
// cmp rdi, 10 // 运行时错误检查
// jge .error_out_of_bounds
// mov eax, [rsi + rdi*4] // 直接访问,无额外运行时检查
// ret
// .error_out_of_bounds:
// mov rax, error.OutOfBounds
// ret
关键说明:
- 编译器在当前函数内进行证明,证明超时则自动插入运行时检查
- 错误路径保留显式检查,确保安全
- 编译器自动消除冗余检查,只保留必要的错误处理逻辑
一句话总结:Uya 的设计哲学 = 坚如磐石 = 程序员提供证明,编译器验证证明,运行时绝对安全;
将运行时的不确定性转化为编译期的确定性,证明超时则自动插入运行时检查。
AI友好设计
为什么 Uya 语言对AI友好?
Uya 语言从设计之初就考虑了AI代码生成和理解的需求,具有以下特点:
简洁明了的语法
- 零 lifetime 符号:无需学习复杂的生命周期标注,降低AI学习成本
- 无隐式控制:所有行为都是显式的,易于AI理解和推理
- 单页纸可读完:规范简洁,AI可以快速掌握语言全貌
- 一致的命名和约定:减少歧义,提高代码生成准确性
可预测的行为
- 编译期证明:所有安全保证在编译期完成,AI生成的代码要么安全,要么无法编译
- 零运行时意外:无未定义行为,AI生成的代码行为可预测
- 清晰的错误信息:编译错误信息明确,帮助AI理解问题所在
易于推理的类型系统
// 类型清晰,无隐式转换
const x: i32 = 10; // 明确类型
const y: f64 = 3.14; // 明确类型
const z: i32 = x + 5; // 类型必须匹配
// 错误处理明确(支持预定义和运行时错误)
fn divide(a: i32, b: i32) !i32 {
if b == 0 {
return error.DivisionByZero; // 预定义错误(可选)
}
if a < 0 {
return error.NegativeInput; // 运行时错误,无需预定义
}
return a / b; // 正常返回值
}
完整的工具链支持
- 丰富的标准库文档:每个函数都有清晰的文档,帮助AI理解用途
- 编译器智能提示:为AI提供上下文信息和补全建议
- 格式化工具:统一的代码风格,减少AI输出歧义
AI友好意味着:人类程序员和AI都能轻松理解、编写和维护 Uya 语言代码。简洁的语法和明确的语义降低了学习和使用的门槛。
类型系统
| Uya 类型 | C 对应 | 大小/对齐 | 备注 |
|---|---|---|---|
i8 i16 i32 i64 |
同宽 signed | 1 2 4 8 B | 对齐 = 类型大小;支持 @max/@min 内置函数访问极值 |
u8 u16 u32 u64 |
同宽 unsigned | 1 2 4 8 B | 对齐 = 类型大小;无符号整数类型 |
f32 f64 |
float/double | 4/8 B | 对齐 = 类型大小 |
bool |
uint8_t | 1 B | 0/1,对齐 1 B |
byte |
uint8_t | 1 B | 无符号字节,对齐 1 B,用于字节数组 |
void |
void | 0 B | 仅用于函数返回类型 |
*byte |
char* | 4/8 B | FFI 指针类型 *T 的一个实例(T=byte),用于 FFI 函数参数和返回值,指向 C 字符串;FFI 指针类型 *T 支持所有 C 兼容类型 |
&T |
普通指针 | 4/8 B(平台相关) | 无 lifetime 符号;32位平台=4B,64位平台=8B;可通过 as 显式转换为 FFI 指针类型 *T(仅用于 FFI 调用);&void 是通用指针类型,可转换为任何指针类型 |
&void |
通用指针 | 4/8 B(平台相关) | 通用指针类型,可以转换为任何指针类型(&void → &T),用于实现类型擦除和通用指针操作;32位平台=4B,64位平台=8B |
&atomic T |
原子指针 | 4/8 B(平台相关) | 关键字驱动;32位平台=4B,64位平台=8B |
atomic T |
原子类型 | sizeof(T) | 语言级原子类型 |
[T: N] |
T[N] | N·sizeof(T) | N 为编译期正整数 |
struct S { } |
struct S | 字段顺序布局 | 对齐 = 最大字段对齐 |
interface I { } |
- | 8/16 B(平台相关) | vtable 指针(4/8B) + 数据指针(4/8B);32位平台=8B,64位平台=16B |
enum E { } |
enum E | sizeof(底层类型) | 枚举类型,默认底层类型为 i32 |
(T1, T2, ...) |
- | 字段顺序布局 | 元组类型,对齐 = 最大字段对齐 |
!T |
错误联合类型 | max(sizeof(T), sizeof(错误标记)) + 对齐填充 | T | Error |
重要特性:无隐式转换;指针类型转换:&T as *T(仅用于 FFI 调用,显式转换)、&void ↔ &T(通用指针转换);指针自动解引用:ptr.field 等价于 (*ptr).field;字段赋值:支持 obj.field = value 和 ptr.field = value;无指针算术;零 lifetime 符号。
类型相关的极值常量
// @max/@min 内置函数访问极值(类型推断)
const MAX: i32 = @max; // 2147483647(从类型注解 i32 推断)
const MIN: i32 = @min; // -2147483648(从类型注解 i32 推断)
// 在溢出检查中使用(从变量类型推断)
const a: i32 = ...;
const b: i32 = ...;
if a > 0 && b > 0 && a > @max - b { // 从 a 和 b 的类型 i32 推断
return error.Overflow; // 运行时错误,无需预定义
}
完整示例
结构体 + 栈数组 + FFI
struct Mat4 {
m: [f32: 16]
}
extern printf(fmt: *byte, ...) i32;
fn print_mat(mat: Mat4) void {
var i: i32 = 0;
while i < 16 {
printf("%f ", mat.m[i]);
i = i + 1;
if (i % 4) == 0 { printf("\n"); }
}
}
fn main() i32 {
var m: Mat4 = Mat4{ m: [0.0: 16] };
m.m[0] = 1.0;
m.m[5] = 1.0;
m.m[10] = 1.0;
m.m[15] = 1.0;
print_mat(m);
return 0;
}
Uya 指针传递给 FFI 函数
// 声明外部 C 函数
extern write(fd: i32, buf: *byte, count: i32) i32;
extern printf(fmt: *byte, ...) i32;
fn main() i32 {
var buffer: [byte: 100] = [];
// Uya 普通指针通过 as 显式转换为 FFI 指针类型
const result: i32 = write(1, &buffer[0] as *byte, 100);
if result < 0 {
printf("Write failed\n");
return 1;
}
return 0;
}
指针转换规则:
- ✅
&T as *T:Uya 普通指针可以显式转换为 FFI 指针类型(安全转换,无精度损失) - ✅
&void ↔ &T:通用指针与具体指针类型之间的转换(类型擦除) - 仅在 FFI 函数调用时使用,符合"显式控制"设计哲学
- 编译期检查,无运行时开销
错误处理 + defer/errdefer
// 错误处理(支持预定义和运行时错误)
error FileError; // 预定义错误(可选)
extern open(path: *byte, flags: i32) i32;
extern close(fd: i32) i32;
extern read(fd: i32, buf: *byte, size: i32) i32;
extern printf(fmt: *byte, ...) i32;
struct File {
fd: i32
}
fn open_file(path: *byte) !File {
const fd: i32 = open(path, 0);
if fd < 0 {
return error.FileError; // 预定义错误
}
// 或者使用运行时错误
if fd == 0 {
return error.InvalidHandle; // 运行时错误,无需预定义
}
return File{ fd: fd };
}
File {
fn drop(self: File) void {
if self.fd >= 0 {
close(self.fd);
}
}
}
fn read_file(path: *byte) !i32 {
const file: File = try open_file(path);
defer {
printf("File operation completed\n");
}
errdefer {
printf("Error occurred, cleaning up\n");
}
var buf: [byte: 1024] = [];
const n: i32 = read(file.fd, &buf[0], 1024);
if n < 0 {
return error.FileError; // 预定义错误
}
// 或者使用运行时错误
if n == 0 {
return error.EmptyFile; // 运行时错误,无需预定义
}
return n;
}
error_id 稳定性:error_id = hash(error_name),相同错误名在任意编译中映射到相同 error_id;hash 冲突时编译器报错并提示冲突名称。
默认安全并发
interface IIncrement {
fn inc(self: &Self) i32;
}
struct Counter : IIncrement {
value: atomic i32
}
Counter {
fn inc(self: &Self) i32 {
self.value += 1; // 自动原子 fetch_add
return self.value; // 自动原子 load
}
}
fn main() i32 {
const counter: Counter = Counter{ value: 0 };
// 多线程并发递增,零数据竞争
// 所有操作自动原子化,无需锁
return 0;
}
for循环迭代
extern printf(fmt: *byte, ...) i32;
fn main() i32 {
const arr: [i32: 5] = [10, 20, 30, 40, 50];
// 基本迭代(只获取元素值)
printf("Basic iteration:\n");
for arr |item| {
printf(" value: %d\n", item);
}
// 整数范围迭代
printf("\nInteger range iteration:\n");
for 0..10 |i| {
printf(" i: %d\n", i); // 输出 0 到 9
}
// 可修改迭代
var arr2: [i32: 5] = [10, 20, 30, 40, 50];
printf("\nMutable iteration:\n");
for arr2 |&item| {
*item = *item * 2; // 修改每个元素
printf(" value: %d\n", *item);
}
return 0;
}
切片语法(类似Python)
extern printf(fmt: *byte, ...) i32;
fn main() i32 {
var arr: [i32: 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
// 基本切片语法:&arr[start:len] 返回切片视图
const slice: &[i32] = &arr[2:5]; // 动态长度切片
printf("Values: ");
for slice |value| {
printf("%d ", value); // 输出:2 3 4 5 6
}
printf("\n");
// 修改原数组影响切片
arr[3] = 99;
printf("After modifying arr[3]=99: ");
for slice |value| {
printf("%d ", value); // 输出:2 99 4 5 6(切片反映原数组变化)
}
printf("\n");
// 可修改迭代
for slice |&ptr| {
*ptr = *ptr * 2; // 修改切片元素,同时影响原数组
}
printf("After doubling slice elements: ");
for slice |value| {
printf("%d ", value); // 输出:4 198 8 10 12
}
printf("\n");
// 已知长度切片
const exact_slice: &[i32: 3] = &arr[0:3];
printf("Exact slice [0:3]: ");
for exact_slice |value| {
printf("%d ", value); // 输出:0 1 2
}
printf("\n");
// 负数索引
const tail: &[i32] = &arr[-3:3]; // 等价于 &arr[7:3]
printf("Tail slice [-3:3]: ");
for tail |value| {
printf("%d ", value); // 输出:7 8 9
}
printf("\n");
// 索引迭代
printf("Index iteration: ");
for slice |i| {
printf("[%d] ", i); // 输出:[0] [1] [2] [3] [4]
}
printf("\n");
return 0;
}
切片作为函数参数
extern printf(fmt: *byte, ...) i32;
// 使用动态长度切片作为参数
fn process(p: &[i32]) void {
printf("Processing slice with length: ");
for p |value| {
printf("%d ", value);
}
printf("\n");
}
// 使用已知长度切片作为参数
fn process_exact(p: &[i32: 3]) void {
printf("Processing exact slice: ");
for p |value| {
printf("%d ", value);
}
printf("\n");
}
// 多个切片参数示例
fn process_multiple(a: i32, p1: &[i32], b: i32, p2: &[f32], c: i32) void {
printf("a=%d, b=%d, c=%d\n", a, b, c);
printf("p1: ");
for p1 |v1| {
printf("%d ", v1);
}
printf("\np2: ");
for p2 |v2| {
printf("%f ", v2);
}
printf("\n");
}
fn main() i32 {
const arr: [i32: 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const floats: [f32: 5] = [1.1, 2.2, 3.3, 4.4, 5.5];
// 传递动态长度切片
process(&arr[2:3]); // 传递切片 &arr[2:3]
// 传递已知长度切片
process_exact(&arr[0:3]); // 传递已知长度切片 &[i32: 3]
// 传递多个切片
process_multiple(10, &arr[1:4], 20, &floats[0:3], 30);
return 0;
}
安全指针算术
extern printf(fmt: *byte, ...) i32;
fn main() i32 {
const arr: [i32: 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
var ptr: &i32 = &arr[0]; // 指向数组首元素的指针
// 基本指针算术
// 指针向后偏移
const offset_ptr: &i32 = ptr + 5; // 指向第6个元素
printf("Value at ptr + 5: %d\n", *offset_ptr); // 输出 5
// 指针算术与边界检查
if offset_ptr < &arr[10] { // 边界检查
const val: i32 = *offset_ptr; // 安全解引用
printf("Value: %d\n", val);
}
// 循环中的指针算术(使用新的 for 循环语法)
for 0..10 |i| {
const current_ptr: &i32 = ptr + i; // 编译器证明 i < 10,安全
printf("arr[%d] = %d\n", i, *current_ptr);
}
// 指针间距离计算
const ptr1: &i32 = &arr[2];
const ptr2: &i32 = &arr[7];
const diff: i32 = (ptr2 as i32) - (ptr1 as i32); // 计算指针间距离
printf("Distance between ptr2 and ptr1: %d\n", diff);
// 安全的指针算术函数
const result: &i32 = safe_ptr_arith(arr, 3) catch |err| {
if err == error.OutOfBounds {
printf("Index out of bounds\n");
}
return 1;
};
printf("Value at safe index: %d\n", *result);
return 0;
}
// 安全的指针算术函数
fn safe_ptr_arith(arr: [i32: 10], offset: i32) !&i32 {
if offset < 0 || offset >= 10 {
return error.OutOfBounds;
}
return &arr[0] + offset; // 编译器证明 offset < 10,安全
}
泛型系统(可选特性)
// 泛型接口定义:使用尖括号
interface Collection<T> {
fn add(self: &Self, item: T) !void;
fn get(self: &Self, index: i32) !T;
fn size(self: &Self) i32;
}
// 泛型结构体定义:使用尖括号,约束紧邻参数
struct ArrayList<T: Default> : Collection<T> {
items: &[T],
capacity: i32,
size: i32
}
// 接口方法作为结构体方法定义
ArrayList<T: Default> {
fn add(self: &Self, item: T) !void {
if self.size >= self.capacity {
return error.Full;
}
self.items[self.size] = item;
self.size = self.size + 1;
}
fn get(self: &Self, index: i32) !T {
if index < 0 || index >= self.size {
return error.OutOfBounds;
}
return self.items[index];
}
fn size(self: &Self) i32 {
return self.size;
}
}
// 泛型算法函数
fn find_index<T>(items: &[T], target: T) i32 {
for 0..len(items) |i| {
if items[i] == target {
return i;
}
}
return -1;
}
fn main() i32 {
// 创建具体类型的 ArrayList
var buffer: [i32: 100] = [0: 100];
const list: ArrayList<i32> = ArrayList<i32>{
items: &buffer[0:100],
capacity: 100,
size: 0
};
// 使用泛型函数
list.add(42);
const index = find_index<i32>(&list.items[0:list.size], 42);
return 0;
}
溢出处理运算符
extern printf(fmt: *byte, ...) i32;
fn main() i32 {
const a: i32 = 2000000000;
const b: i32 = 2000000000;
// 使用 try 关键字检查溢出(返回 error.Overflow)
const result1: !i32 = try a + b;
catch |err| {
if err == error.Overflow {
printf("Overflow detected with try\n");
}
return 1;
}
printf("Result with try: %d\n", result1);
// 使用饱和运算符(溢出时返回极值)
const result2: i32 = a +| b; // 返回 @max (2147483647)
printf("Result with +|: %d\n", result2);
// 使用包装运算符(溢出时包装)
const result3: i32 = a +% b; // 包装后的值
printf("Result with +%: %d\n", result3);
// 减法示例
const c: i32 = -2000000000;
const d: i32 = 2000000000;
const result4: i32 = c -| d; // 饱和减法,返回 @min
const result5: i32 = c -% d; // 包装减法
// 乘法示例
const e: i32 = 100000;
const f: i32 = 100000;
const result6: !i32 = try e * f; // 检查溢出
const result7: i32 = e *| f; // 饱和乘法
const result8: i32 = e *% f; // 包装乘法
return 0;
}
标准库(最小集)
| 函数/运算符 | 签名/语法 | 说明 |
|---|---|---|
@len |
fn @len(a: [T: N]) i32 |
返回数组元素个数 N(编译期常量)。对于空数组字面量 [],返回数组声明时的大小,而不是 0 |
@size_of |
fn @size_of(T) i32 |
返回类型 T 的字节大小(编译期常量,以 @ 开头) |
@align_of |
fn @align_of(T) i32 |
返回类型 T 的对齐字节数(编译期常量,以 @ 开头) |
try 关键字 |
try a + b |
检查溢出,溢出返回 error.Overflow |
+| -| *| |
a +| b |
饱和运算符,溢出返回极值 |
+% -% *% |
a +% b |
包装运算符,溢出返回包装值 |
说明:@len、@size_of、@align_of、@max、@min 为内置函数(以 @ 开头),try 关键字和运算符是编译器内置的,编译期展开,自动可用,无需导入或声明。
@size_of 和 @align_of 示例
一句话总结
Uya = AI友好 · 默认即高级内存安全 + 并发安全 + 显式错误处理 + 泛型系统(<T: Bound>) + 统一结构体标准(C内存布局+完整Uya能力) + 切片类型系统 + 函数指针类型 + 枚举类型系统 + 元组类型系统 + 指针类型转换(&T as *T) + 结构体默认值语法 + 异步编程基础设施;
只加 1 个关键字 atomic T,零 lifetime 符号,零 GC;
所有 UB 必须被编译期证明为安全 → 失败即编译错误;
编译期证明(本函数内),证明超时则自动插入运行时检查,不降级、不插运行时锁。