为啥要瞅瞅仓颉这玩意儿?
有一说一,现在的编程语言多得跟米一样,对吧?那一门新语言想火,没点绝活儿肯定不行。仓颉(Cangjie)这哥们儿,是华为搞出来的新玩意儿,静态编译的,主打的就是一个现代化、性能炸裂、安全感满满,而且天生就会搞并发。就凭这几点,已经有不少大佬开始关注了。
这篇博客呢,就是你的"老司机"指南,带你把仓颉这车开得明明白白。不管你是刚上路的小白,还是开惯了 Rust、Go、Java、Node.js 的老司机,都能在这儿找到感觉。咱就从最简单的"Hello, World!"开整,一步步把它那些牛逼的特性,比如啥类型系统、并发模型都给扒个底朝天。走了您内!
一:刚认识,混个脸熟 ------ 装一下,跑个 Demo
万事开头难?不存在的,仓颉上手简直不要太丝滑。
1. 安装,搞定环境
整个安装过程跟其他新潮语言差不多,用官方给的工具 cj
一条龙服务,轻松搞定。
-
逛官网 : 直接去仓颉官网下载页。
-
跑脚本: 看你电脑是啥系统(Windows, macOS, Linux),复制粘贴对应的命令就完事儿了。
比如你是 Linux/macOS
$ curl --proto '=https' --tlsv1.2 -sSf https://cangjie-lang.cn/install.sh | sh
装完之后,把路径配一下,就能在命令行里使唤 cj
了。
$ cj --version
cj version x.x.x
2. 你好啊,仓颉!
老规矩,咱得让它跟世界打个招呼。
新建一个 main.cj
文件,把下面这几行代码粘进去:
// main.cj
// 意思是从 'std::io' 这个库里,把 'println' 这函数拿来用
use std::io;
// 这就是程序的入口,从这儿开始跑
public func main(): void {
io::println("你好,仓颉!");
}
给你白话白话:
-
use std::io;
: 就是告诉编译器,"嘿,我要用标准库(std
)里管输入输出(io
)的工具了"。跟 Rust 的use
或者 Java 的import
一个意思。 -
public func main(): void
: 这就是程序的启动点。-
public
: 公开的,谁都能调用。 -
func
: 关键字,意思是"这是个函数"。 -
main
: 函数名,main
函数就是老大,程序从它开始执行。 -
(): void
: 括号里没东西,说明没参数;void
意思是这函数干完活儿不给回信儿(没返回值)。
-
编译跑起来:
打开你的黑框框(终端),用 cj
命令编译运行一把。
# 编译顺便跑一下 main.cj
$ cj run main.cj
你好,仓颉!
图解一下这过程:
+------------+ 一顿操作 +-----------------+ 跑一下 +----------------+
| main.cj | (cj build) > | 一个能跑的文件 | (cj run) > | 屏幕上出现 |
| (你的代码) | | (比如 main.exe) | | "你好,仓颉!" |
+------------+ +-----------------+ +----------------+
二:核心玩法 ------ 变量、类型和流程控制
基础整明白了,咱再往里看看它的筋骨。
1. 变量和数据类型
记住,仓颉是门静态强类型语言。说人话就是,你定义一个变量是啥类型,它这辈子就是啥类型,在跑之前就得定好。
public func main(): void {
// 声明个变量,存点东西
// let 声明的是个常量,不能改
let message: string = "这是一个常量,铁打的";
// var 声明的是个变量,想改就改
var count: int = 0;
count = count + 1; // 没毛病
// 它还挺聪明,能自己猜类型
let implicit_message = "我不用说,它也知道我是字符串"; // 自动猜出是 string
var implicit_count = 10; // 自动猜出是 int
}
跟别的语言比比看:
-
Rust : 不能说像,简直是一毛一样。Rust 用
let
和let mut
。仓颉的let
和var
就是这思路,主打一个"默认不让你乱改",安全第一。 -
Go : Go 用
var
和const
。var count int = 0
这样。Go 那个:=
写法(比如count := 0
)就跟仓颉的类型推断差不多。 -
Java : Java 里变量随便改,比如
int count = 0;
。想不让改得加个final
。 -
Node.js (JavaScript) : JS 那就随性了,动态类型。
const
是常量,let
是变量,但类型是跑起来才知道。
几个常用的数据类型:
仓颉类型 | 大白话 | 例子 |
---|---|---|
int |
就是整数 | 42 |
float64 |
带小数点的数 | 3.14159 |
bool |
就俩值,对或错 | true , false |
string |
一串字儿 | "你好" |
char |
单个字或符号 | 'A' |
2. 流程控制
这玩意儿就是指挥程序往哪儿走的。仓颉给的还是老三样 if-else
, for
, while
。
If-Else 表达式:
在仓颉里,if-else
不光能判断,还能直接"吐"个值出来。
let number: int = 10;
let description: string = if number % 2 == 0 {
"偶数"
} else {
"奇数"
};
// 这下 description 就是 "偶数" 了,贼方便
这点跟 Rust 学的,代码写起来特别爽。不像 Java 和 Go,if-else
干完活就走了,还得你自己再整个变量去接。
循环:
// For 循环 (专门用来过一遍列表啥的)
let numbers: Array<int> = [1, 2, 3, 4, 5];
for num in numbers {
// 对每个 num 做点啥
}
// While 循环
var i: int = 0;
while i < 5 {
// ...
i = i + 1;
}
仓颉的 for
循环就干一件事:遍历。简单直接。这跟 Go 的 for ... range
和 Rust 的 for ... in
是一伙的,目的就是让你少写错代码,不像 C/Java 那种老式 for 循环,一不小心就写出界了。
三:秀操作 ------ 函数、结构体和并发
行,热身结束,咱来看看仓颉那些更骚的操作。
1. 函数
函数嘛,就是把一堆代码打包成一个块,方便重复使用。
// 收俩整数,还一个它俩的和
func add(a: int, b: int): int {
return a + b;
}
// 用一下
let sum: int = add(5, 3); // sum 现在就是 8
2. 结构体 (Struct) 和方法 (Method)
想自己定义数据类型?用 struct
就对了。跟 Go 和 Rust 的 struct
差不多。
// 定义一个"点"
struct Point {
x: float64,
y: float64,
}
// 给这个"点"加点技能
impl Point {
// 'self' 就代表这个"点"自己
public func distance_from_origin(self): float64 {
return (self.x.pow(2) + self.y.pow(2)).sqrt();
}
}
// 走一个
let p = Point{x: 3.0, y: 4.0};
let dist = p.distance_from_origin(); // dist 就是 5.0
图解一下:
+--------------------------+
| struct Point { x, y } | <-- 这是"长啥样"(数据)
+--------------------------+
|
| 配套技能
V
+--------------------------+
| impl Point { |
| func ...() | <-- 这是"能干啥"(方法)
| } |
+--------------------------+
这种把"数据"和"行为"分开定义但又绑定的玩法,是现在很流行的模式(比如 Rust)。比 Java 那种啥都塞一个 class 里的要灵活,比 Go 那种"非侵入式"的接口用起来更直接。
3. 并发:协程 (Coroutine)
并发,这可是仓颉的王牌!它用的协程模型,超级轻量,跟 Go 语言的 Goroutine 简直是亲兄弟。
用 async
和 await
关键字,写异步代码就跟写普通代码一样顺溜。
// 模拟一个花时间的活儿,比如上网扒数据
async func fetch_data(url: string): string {
// ... 假装在请求网络
std::time::sleep(1.second); // 睡一秒
return "从 " + url + " 搞来的数据";
}
public async func main() {
// 同时安排两个活儿
let task1 = async fetch_data("url1");
let task2 = async fetch_data("url2");
// 等它俩干完
let data1 = await task1;
let data2 = await task2;
io::println(data1);
io::println(data2);
}
跟其他语言的并发比划比划:
-
Go : 仓颉的协程和 Go 的 Goroutine 理念上贼像。Go 用
go
关键字开个小差,用channel
通信。仓颉的async/await
写起来更像普通代码,对从 JS/Python/C# 转过来的兄弟们更友好。 -
Node.js : Node.js 是单线程跑异步。仓颉的
async/await
跟 Node.js 的语法和感觉一模一样。但!底层完全不同:仓颉是真的能把活儿分给不同 CPU 核心去干,而 Node.js 默认就在一个线程里倒腾。 -
Java : Java 以前搞并发都靠线程(
Thread
),那玩意儿太重了。虽然现在 Java 也有了"虚拟线程"这种轻量级的玩意儿,但仓颉和 Go 这种天生就支持协程的,设计上更纯粹、更简单。 -
Rust : Rust 的
async/await
跟仓颉也像,但 Rust 让你自己选配一个"异步发动机"(比如 Tokio)。仓颉很可能给你内置了一个牛逼的,省得你操心了。
图解:仓颉/Go vs Java/传统线程
仓颉/Go 模型:
+----------------------------------------+
| 电脑的CPU核心 1 | 核心 2 | ... | 核心 N |
+----------------------------------------+
| | |
+-----+ +-----+ +-----+
|小任务A| |小任务D| |小任务G|
+-----+ +-----+ +-----+
|小任务B| |小任务E|
+-----+ +-----+
|小任务C| |小任务F|
+-----+ +-----+
// 一大堆轻量级的小任务,在几个CPU核心上灵活切换,成本贼低
传统 Java 线程模型:
+----------------------------------------+
| CPU核心 A (Java线程A) | CPU核心 B (Java线程B) |
+----------------------------------------+
// 一个Java线程就占一个CPU核心,创建和切换都费劲
四:高手进阶 ------ 高级特性和圈子
1. 错误处理
仓颉处理错误的方式很直接,跟 Go 和 Rust 想法一样,就是把"错误"当成正经事儿来处理,不让你稀里糊涂地就过去了。
Go
// 这函数可能会失败,它会返回一个 Result
func might_fail(should_fail: bool): Result<string, Error> {
if should_fail {
return Err(Error{"出错了兄弟"});
} else {
return Ok("搞定!");
}
}
// 处理这个 Result
let result = might_fail(true);
match result {
case Ok(data) => io::println("拿到数据: " + data),
case Err(e) => io::println("坏了,出错了: " + e.message),
}
```Result<T, E>` 这玩意儿清楚地告诉你:这函数要么成功,给你个 `T` (`Ok(T)`);要么失败,给你个 `E` (`Err(E)`)。然后用 `match` 强制你把两种情况都考虑到,再也不会有漏网之鱼了。
2. 内存管理
仓颉又想快又想安全,那内存管理肯定得下功夫。它很可能学了 Rust 那套"所有权"和"借用"的玩法,或者用了啥牛逼的自动内存管理技术。反正目标就是,既不像 C/C++ 那样让你手动管内存搞得心累,也别像传统 GC 那样时不时卡一下。
- 圈子和未来
一门语言火不火,得看圈子大不大。仓颉现在还算是个小萌新,但后台硬啊。可以期待一下:
- 标准库: 自带一堆好用的工具。
- 包管理器: 像 `cargo` 或 `go mod` 那样,管理第三方库。
- 工具链: 好用的编译器、代码提示、调试器啥的。
- 兼容容性: 能跟老大哥 C/C++ 一起玩。
总结:仓颉这小子,到底想干啥?
仓颉不是瞎抄,它是把各家之长都学过来,然后自己攒了个大招。
|------|------------------|------------------|-------------|-------------|------------------|
| 特性 | 仓颉 (Cangjie) | Rust | Go | Java | Node.js(JS) |
| 类型 | 静态, 编译, 强类型 | 静态, 编译, 强类型 | 静态, 编译, 强类型 | 静态, 编译, 强类型 | 动态, 解释/JIT |
| 新能 | 起飞** (目标) | 起飞 | 很快 | 还行 | 易班 |
| 安全 | 拉满(目标) | 拉满 (所有权) | 高(GC) | 高(GC) | 高(GC) |
| 并发 | 协程 (async/await) | 协程 (async/await) | Goroutine | 线程/虚拟线程 | 事件循环 |
| 风格 | 潮,像Rust/Swift | C++和函数式的混血 | C语言风, 极简 | C++风, 啰嗦 | C语言风, 灵活 |
| 错误处理 | Result | Result | 多返回值 | 异常 | 回调/Promise/async |
| 主场 | 系统, 云原生, 嵌入式 | 系统, WebAssembly | 云原生, 后端 | 企业应用, 安卓 | Web后端, 工具 |
2、语言画像
如果你从 Rust 来: 你会爱上仓颉对安全和性能的追求,还有那些熟悉的语法。而且它可能比 Rust 更"傻瓜化",不用你操心运行时的事。
Gopher: 你会对它的并发和编译速度感到很亲切。但仓颉更严格的类型和错误处理,可能会让你觉得代码更稳了。
如果你从 Java 来: 仓颉会让你知道啥叫轻量级并发,啥叫不写废话。从重重的"对象"到更灵活的"数据",得换换脑子。
如果你从 Node.js 来: `async/await` 你肯定玩得贼溜。但仓颉的静态类型能在编译时就帮你把好多 bug 揪出来。而且,你能真正用上电脑的所有核心,火力全开!
总结
仓颉这门语言,野心不小。它想在性能、安全、开发爽度这三个点上,找到一个完美的平衡。它吸收了过去这些年编程语言发展的精华,就是为了给未来的软件开发(特别是系统和云原生这块)打个好底子。
学仓颉,不光是学个新语言,更是了解一下现在软件开发都在愁啥、有啥新招。现在上车,绝对是早期股东,一起见证它牛逼就完事儿了!