鸿蒙PC:Linux 搭建 Rust 开发环境并实现计算器项目
本文介绍了在鸿蒙PC上搭建Rust开发环境并实现计算器项目的完整方案。项目采用分层架构:ArkTS负责UI层,C++ NAPI作为Native桥接层,Rust处理核心计算逻辑,通过CMake将Rust静态库链接到鸿蒙应用。文章详细讲解了环境配置、项目结构、ABI适配等关键步骤,并提供了完整的代码仓库。该项目展示了一种可复用的ArkTS+C+++Rust技术路线,特别适用于需要高性能计算的鸿蒙应用场
鸿蒙PC:Linux 搭建 Rust 开发环境并实现计算器项目
前言
Rust 近几年在系统开发、跨平台 Native 模块、命令行工具和高性能后端中使用越来越多。对于鸿蒙 PC 应用来说,Rust 并不是直接替代 ArkTS,而是更适合承担核心计算逻辑、复杂算法、可复用业务模块这类稳定、可测试、对性能和安全性要求更高的部分。
本文通过一个可以在 DevEco Studio 模拟器运行的计算器项目,演示如何把 Rust 接入鸿蒙 PC 应用。整体方案是:
- ArkTS 负责页面和交互。
- C++ NAPI 负责鸿蒙侧 Native 桥接。
- Rust 负责计算器核心运算。
- CMake 负责把 Rust 静态库链接进
libentry.so。 - DevEco Studio 负责编译、打包和安装到模拟器。
本文项目已经按
arm64-v8a和x86_64两种 ABI 配置,避免 PC 模拟器安装时报install parse native so failed。

图片说明:这里保留运行效果图位置。发布前建议替换为 DevEco Studio 模拟器中计算器页面截图。
项目仓库地址: AtomGit仓库地址
一、项目目标
1.1 实现效果
本项目实现一个基础计算器,支持:
- 数字输入。
- 小数点输入。
- 加、减、乘、除。
- 清空。
- 退格。
- 正负号切换。
- 除零错误提示。
- 鸿蒙 PC 模拟器运行。
项目不是单纯 ArkTS 实现,而是把核心计算放在 Rust 中完成。这样文章重点不只是“做一个计算器”,而是展示一条可复用的 ArkTS + C++ NAPI + Rust 技术路线。
1.2 技术架构
整体调用链如下:
ArkTS UI
↓ import { calculate } from 'libentry.so'
C++ NAPI
↓ rust_calculate(left, right, operator)
Rust staticlib
↓ calculate + format_number
返回字符串结果
这种结构的优点是分层明确:
| 层级 | 技术 | 职责 |
|---|---|---|
| UI 层 | ArkTS | 页面布局、按钮事件、状态管理 |
| Native 桥接层 | C++ NAPI | 参数转换、Native 方法导出 |
| 核心逻辑层 | Rust | 运算、错误处理、结果格式化 |
| 构建层 | CMake + Cargo | 构建 Rust 静态库并链接到 so |
二、环境准备
2.1 基础工具
需要准备以下工具:
| 工具 | 作用 | 建议 |
|---|---|---|
| DevEco Studio | 鸿蒙应用开发 IDE | 使用已安装 OpenHarmony/HarmonyOS SDK 的版本 |
| Rust | 编译 Rust 核心库 | 使用 rustup 安装 |
| CMake/Ninja | Native 构建 | DevEco SDK 内置 |
| hdc | 模拟器安装调试 | DevEco SDK 内置 |
| PowerShell/Terminal | 执行构建命令 | Windows 下使用 PowerShell 即可 |
本文实际工程路径建议使用英文目录:
D:\Desktop\HarmonyRustPcApp
不要把 DevEco 工程放在中文路径,例如:
D:\Desktop\rust环境搭建\HarmonyRustPcApp
DevEco/Hvigor 对项目路径有限制,中文路径会导致构建失败:
Invalid project path
2.2 安装 Rust
在 PowerShell 中检查 Rust:
rustc --version
cargo --version
rustup --version
如果命令不可用,需要把 Rust 默认安装目录加入 PATH:
C:\Users\你的用户名\.cargo\bin
本文示例机器中路径为:
C:\Users\GZX\.cargo\bin
2.3 安装鸿蒙 Rust target
鸿蒙 PC 模拟器和真机需要不同 ABI。建议同时安装:
rustup target add x86_64-unknown-linux-ohos
rustup target add aarch64-unknown-linux-ohos
如果下载慢,可以临时使用国内镜像:
$env:RUSTUP_DIST_SERVER='https://mirrors.ustc.edu.cn/rust-static'
rustup target add x86_64-unknown-linux-ohos
rustup target add aarch64-unknown-linux-ohos
验证已安装 target:
rustup target list --installed
期望看到:
aarch64-unknown-linux-ohos
x86_64-unknown-linux-ohos
x86_64-pc-windows-msvc
三、项目目录结构
3.1 工程结构
计算器项目目录如下:
HarmonyRustPcApp
├── AppScope
│ └── app.json5
├── build-profile.json5
├── hvigorfile.ts
├── oh-package.json5
└── entry
├── build-profile.json5
├── hvigorfile.ts
├── oh-package.json5
└── src
└── main
├── cpp
│ ├── CMakeLists.txt
│ ├── napi_init.cpp
│ ├── rust-core
│ │ ├── Cargo.toml
│ │ └── src
│ │ └── lib.rs
│ └── types
│ └── libentry
│ └── index.d.ts
├── ets
│ ├── entryability
│ │ └── EntryAbility.ets
│ └── pages
│ └── Index.ets
├── module.json5
└── resources
3.2 关键文件说明
| 文件 | 作用 |
|---|---|
Index.ets |
计算器页面和交互状态 |
napi_init.cpp |
导出 Native 方法给 ArkTS |
lib.rs |
Rust 计算逻辑 |
Cargo.toml |
Rust 静态库配置 |
CMakeLists.txt |
调用 Cargo 并链接 Rust 静态库 |
index.d.ts |
给 ArkTS 提供 NAPI 类型声明 |
entry/build-profile.json5 |
Native ABI 配置 |
四、配置鸿蒙 Native ABI
4.1 为什么要配置 ABI
如果 HAP 里只包含 arm64-v8a/libentry.so,但当前设备或模拟器是 x86_64,安装时会报错:
Install Failed: error: failed to install bundle.
code:9568347
error: install parse native so failed.
In the module named entry, the Abi type supported by the device does not match the Abi type configured in the C++ project.
这个错误不是 Rust 代码错误,而是设备 ABI 和 HAP 内 Native so ABI 不匹配。
4.2 正确配置 abiFilters
修改 entry/build-profile.json5:
{
"apiType": "stageMode",
"buildOption": {
"externalNativeOptions": {
"path": "./src/main/cpp/CMakeLists.txt",
"arguments": "",
"cppFlags": "",
"abiFilters": [
"arm64-v8a",
"x86_64"
]
}
},
"targets": [
{
"name": "default"
}
]
}
这样构建后会同时生成:
entry/build/default/intermediates/libs/default/arm64-v8a/libentry.so
entry/build/default/intermediates/libs/default/x86_64/libentry.so
PC 模拟器常见 ABI 是
x86_64,真机或 ARM 模拟器常见 ABI 是arm64-v8a。开发阶段建议两个都打包,减少部署失败。
五、Rust 核心计算模块
5.1 Cargo.toml 配置
Rust 模块以 staticlib 形式输出,供 C++ 链接:
[package]
name = "rust_core"
version = "0.1.0"
edition = "2021"
[lib]
name = "rust_core"
crate-type = ["staticlib"]
[profile.release]
lto = true
codegen-units = 1
panic = "abort"
这里重点是:
crate-type = ["staticlib"]:输出静态库。panic = "abort":Native 场景下减少 unwinding 风险。lto = true:发布构建启用链接优化。
5.2 Rust 运算逻辑
Rust 中处理加、减、乘、除,并对除零和无效操作符返回明确错误:
fn calculate(left: f64, right: f64, operator: &str) -> String {
match operator {
"+" => format_number(left + right),
"-" => format_number(left - right),
"*" => format_number(left * right),
"/" => {
if right.abs() < f64::EPSILON {
"Cannot divide by zero".to_string()
} else {
format_number(left / right)
}
}
_ => "Invalid operator".to_string(),
}
}
结果格式化函数会去掉多余小数尾零:
fn format_number(value: f64) -> String {
if !value.is_finite() {
return "Error".to_string();
}
let mut text = format!("{value:.10}");
while text.contains('.') && text.ends_with('0') {
text.pop();
}
if text.ends_with('.') {
text.pop();
}
if text == "-0" {
return "0".to_string();
}
text
}
5.3 FFI 导出函数
C++ 无法直接调用 Rust 的普通函数,需要通过 C ABI 导出:
#[no_mangle]
pub unsafe extern "C" fn rust_calculate(
left: c_double,
right: c_double,
operator: *const c_char,
) -> *mut c_char {
if operator.is_null() {
return into_c_string("Invalid operator".to_string());
}
let operator = CStr::from_ptr(operator).to_string_lossy();
into_c_string(calculate(left, right, operator.as_ref()))
}
Rust 返回的是 C 字符串指针,因此还需要提供释放函数:
#[no_mangle]
pub unsafe extern "C" fn rust_free_c_string(value: *mut c_char) {
if !value.is_null() {
let _ = CString::from_raw(value);
}
}
只要 Rust 通过
CString::into_raw()把内存交给 C/C++,就必须提供对应释放函数。否则 Native 层频繁调用会产生内存泄漏。
六、C++ NAPI 桥接层
6.1 声明 Rust 函数
C++ 中先声明 Rust 导出的 C ABI 函数:
extern "C" {
char *rust_calculate(double left, double right, const char *operator_text);
void rust_free_c_string(char *value);
}
6.2 参数转换
ArkTS 调用 Native 方法时,参数类型是 napi_value。需要转换为 C++ 类型:
static double GetDoubleArg(napi_env env, napi_value value)
{
double result = 0.0;
napi_get_value_double(env, value, &result);
return result;
}
字符串参数转换:
static std::string GetStringArg(napi_env env, napi_value value)
{
size_t length = 0;
napi_get_value_string_utf8(env, value, nullptr, 0, &length);
std::vector<char> buffer(length + 1);
napi_get_value_string_utf8(env, value, buffer.data(), buffer.size(), &length);
return std::string(buffer.data(), length);
}
6.3 导出 calculate 方法
Native 方法接收三个参数:左操作数、右操作数、运算符。
static napi_value Calculate(napi_env env, napi_callback_info info)
{
size_t argc = 3;
napi_value args[3] = {nullptr, nullptr, nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
double left = argc > 0 ? GetDoubleArg(env, args[0]) : 0.0;
double right = argc > 1 ? GetDoubleArg(env, args[1]) : 0.0;
std::string operatorText = argc > 2 ? GetStringArg(env, args[2]) : "";
char *message = rust_calculate(left, right, operatorText.c_str());
napi_value result = nullptr;
napi_create_string_utf8(env, message == nullptr ? "Error" : message, NAPI_AUTO_LENGTH, &result);
rust_free_c_string(message);
return result;
}
6.4 注册 NAPI 模块
static napi_value Init(napi_env env, napi_value exports)
{
napi_property_descriptor descriptors[] = {
{"calculate", nullptr, Calculate, nullptr, nullptr, nullptr, napi_default, nullptr},
};
napi_define_properties(env, exports, sizeof(descriptors) / sizeof(descriptors[0]), descriptors);
return exports;
}
这里的 calculate 就是 ArkTS 侧最终导入的方法。
七、ArkTS 页面实现
7.1 声明 Native 类型
为了让 ArkTS 识别 libentry.so 中的函数,需要提供类型声明:
export const calculate: (left: number, right: number, operator: string) => string;
declare const _default: {
calculate: typeof calculate;
};
export default _default;
路径为:
entry/src/main/cpp/types/libentry/index.d.ts
7.2 ArkTS 导入 Native 方法
页面中使用命名导入:
import { calculate } from 'libentry.so';
不要把 Native 模块当成无类型对象随意调用,否则 ArkTS 严格模式可能报:
Use explicit types instead of "any", "unknown"
7.3 页面状态设计
计算器页面维护几个核心状态:
@State display: string = '0';
@State expression: string = '';
@State storedValue: number | null = null;
@State pendingOperator: string = '';
@State shouldResetDisplay: boolean = false;
@State hasError: boolean = false;
字段说明:
| 状态 | 作用 |
|---|---|
display |
当前显示内容 |
expression |
上方表达式提示 |
storedValue |
已输入的左操作数 |
pendingOperator |
等待执行的运算符 |
shouldResetDisplay |
下一次输入是否重置屏幕 |
hasError |
当前是否为错误状态 |
7.4 调用 Rust 完成计算
当用户点击运算符或等号时,ArkTS 调用 Native:
const result: string = calculate(this.storedValue, currentValue, this.pendingOperator);
如果 Rust 返回错误字符串,页面进入错误状态:
if (result.startsWith('Cannot') || result.startsWith('Invalid') || result === 'Error') {
this.setError(result);
return;
}
这样 UI 层不需要重复实现除零和无效运算判断,核心规则集中在 Rust 中。
八、CMake 调用 Cargo 构建 Rust
8.1 选择 Rust target
CMake 根据鸿蒙 ABI 选择 Rust target:
if(CMAKE_OHOS_ARCH_ABI STREQUAL "arm64-v8a")
set(RUST_TARGET "aarch64-unknown-linux-ohos")
elseif(CMAKE_OHOS_ARCH_ABI STREQUAL "x86_64")
set(RUST_TARGET "x86_64-unknown-linux-ohos")
elseif(CMAKE_OHOS_ARCH_ABI STREQUAL "armeabi-v7a")
set(RUST_TARGET "armv7-unknown-linux-ohos")
else()
message(FATAL_ERROR "Unsupported OHOS ABI for Rust: ${CMAKE_OHOS_ARCH_ABI}")
endif()
8.2 查找 cargo
DevEco Studio 启动 CMake 时不一定继承系统 PATH,因此 CMake 里需要兜底查找 cargo:
find_program(CARGO_EXECUTABLE cargo)
if(NOT CARGO_EXECUTABLE AND CMAKE_HOST_WIN32 AND EXISTS "$ENV{USERPROFILE}/.cargo/bin/cargo.exe")
set(CARGO_EXECUTABLE "$ENV{USERPROFILE}/.cargo/bin/cargo.exe")
endif()
if(NOT CARGO_EXECUTABLE)
message(FATAL_ERROR "Could not find cargo. Add CARGO_HOME/bin to PATH or install Rust with rustup.")
endif()
8.3 构建 Rust 静态库
add_custom_command(
OUTPUT ${RUST_STATIC_LIB}
COMMAND ${CARGO_EXECUTABLE} build
--manifest-path ${RUST_CRATE_DIR}/Cargo.toml
--release
--target ${RUST_TARGET}
--target-dir ${RUST_TARGET_DIR}
DEPENDS ${RUST_CRATE_DIR}/Cargo.toml ${RUST_CRATE_DIR}/src/lib.rs
WORKING_DIRECTORY ${RUST_CRATE_DIR}
COMMENT "Building Rust static library for ${RUST_TARGET}"
)
最后把 Rust 静态库链接进 libentry.so:
add_library(entry SHARED napi_init.cpp)
target_link_libraries(entry PUBLIC rust_core libace_napi.z.so)
九、构建与运行
9.1 安装依赖
在项目根目录执行:
ohpm install
9.2 构建 HAP
hvigorw --mode module -p module=entry assembleHap --stacktrace
构建成功会看到:
BUILD SUCCESSFUL
HAP 输出位置:
entry/build/default/outputs/default/entry-default-unsigned.hap
9.3 验证 Native so
构建成功后应同时存在:
entry/build/default/intermediates/libs/default/arm64-v8a/libentry.so
entry/build/default/intermediates/libs/default/x86_64/libentry.so
如果只存在 arm64-v8a,PC 模拟器安装时很可能失败。
9.4 DevEco Studio 运行
运行步骤:
- 使用 DevEco Studio 打开英文路径项目。
- 等待 Sync 完成。
- 选择 PC/2in1 模拟器。
- 点击 Run。
- 如果提示签名问题,启用自动签名。
十、常见问题与解决方案
10.1 中文路径导致构建失败
错误示例:
Invalid project path
解决方式:把工程移动到英文路径,例如:
D:\Desktop\HarmonyRustPcApp
10.2 CMake 找不到 cargo
错误示例:
Could not find CARGO_EXECUTABLE using the following names: cargo
解决方式:
- 把
C:\Users\你的用户名\.cargo\bin加入 PATH。 - 重启 DevEco Studio。
- 在 CMake 中增加 cargo 路径兜底。
10.3 缺少 Rust OHOS target
错误示例:
can't find crate for `std`
the `aarch64-unknown-linux-ohos` target may not be installed
解决方式:
rustup target add aarch64-unknown-linux-ohos
rustup target add x86_64-unknown-linux-ohos
10.4 安装时报 native so ABI 不匹配
错误示例:
install parse native so failed
Abi type supported by the device does not match the Abi type configured in the C++ project.
解决方式:在 entry/build-profile.json5 中配置:
"abiFilters": [
"arm64-v8a",
"x86_64"
]
10.5 ArkTS 报 any/unknown 类型错误
错误示例:
Use explicit types instead of "any", "unknown"
解决方式:
- 提供
entry/src/main/cpp/types/libentry/index.d.ts。 - 使用命名导入。
- 给 Native 调用结果显式标注类型。
import { calculate } from 'libentry.so';
const result: string = calculate(1, 2, '+');
十一、项目验证结果
11.1 Rust 单元测试
Rust 侧单元测试覆盖:
- 整数结果格式化。
- 小数结果格式化。
- 加减乘除。
- 除零错误。
- 无效操作符。
测试命令:
cargo test --manifest-path entry/src/main/cpp/rust-core/Cargo.toml
通过结果:
running 3 tests
test tests::calculates_basic_operations ... ok
test tests::formats_integer_like_results_without_decimal_tail ... ok
test tests::reports_invalid_operations ... ok
11.2 HAP 构建验证
构建命令:
hvigorw --mode module -p module=entry assembleHap --stacktrace
验证结果:
BUILD SUCCESSFUL
产物示例:
entry-default-unsigned.hap
11.3 ABI 产物验证
构建后检查:
arm64-v8a/libentry.so
x86_64/libentry.so
这说明项目可以覆盖 ARM 设备和 PC 模拟器两类常见运行环境。
十二、扩展方向
12.1 支持更复杂表达式
当前计算器是二元运算模型:
left operator right
后续可以在 Rust 中实现表达式解析,例如:
1 + 2 * (3 + 4)
可选方案:
- 手写 tokenizer。
- 使用 shunting-yard 算法。
- 在 Rust 中维护表达式 AST。
12.2 支持历史记录
可以在 ArkTS 中维护历史记录列表:
interface HistoryItem {
expression: string;
result: string;
}
也可以把历史记录持久化到本地存储,提升工具完整度。
12.3 支持科学计算
Rust 层可以继续增加:
- 平方。
- 开方。
- 百分比。
- 三角函数。
- 幂运算。
- 取余。
这些逻辑放在 Rust 中更容易测试,也方便复用到其他平台。
十三、工程实践建议
13.1 Rust 负责稳定核心
适合放进 Rust 的内容包括:
- 数学计算。
- 文本解析。
- 加密解密。
- 文件格式处理。
- 协议解析。
- 跨平台核心业务规则。
不建议把所有 UI 状态都搬到 Rust。UI 状态通常和 ArkUI 组件生命周期强相关,放在 ArkTS 中更自然。
13.2 Native 边界要清晰
ArkTS 和 Rust 之间不要传递复杂对象。初期建议使用:
- number
- string
- boolean
- 简单 JSON 字符串
复杂数据可以先序列化成 JSON,再由 Rust 解析。这样调试成本更低。
总结
本文完成了一个鸿蒙 PC Rust 计算器项目。项目采用 ArkTS 编写界面,C++ NAPI 负责 Native 桥接,Rust 实现核心四则运算,并通过 CMake/Cargo 集成进 DevEco Studio 构建流程。
关键经验有三点:
- 鸿蒙 Native 项目必须重视 ABI,PC 模拟器通常需要
x86_64。 - Rust 接入鸿蒙时要同时配置
aarch64-unknown-linux-ohos和x86_64-unknown-linux-ohos。 - DevEco 工程路径建议使用英文路径,避免 Hvigor 路径校验失败。
这个项目虽然是计算器,但已经覆盖了 Rust 接入鸿蒙 PC 应用的核心链路。后续可以在这个基础上扩展科学计算器、表达式解析器、历史记录、主题切换或更复杂的 Rust 算法模块。
如果这篇文章对你有帮助,欢迎点赞、收藏、关注。你的支持是我持续分享鸿蒙、Rust 和跨平台 Native 开发实践的动力。
相关资源
更多推荐




所有评论(0)