鸿蒙PC:Linux 搭建 Rust 开发环境并实现计算器项目

鸿蒙PC:Linux 搭建 Rust 开发环境并实现计算器项目 鸿蒙PCLinux 搭建 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/NinjaNative 构建DevEco SDK 内置hdc模拟器安装调试DevEco SDK 内置PowerShell/Terminal执行构建命令Windows 下使用 PowerShell 即可本文实际工程路径建议使用英文目录D:\Desktop\HarmonyRustPcApp不要把 DevEco 工程放在中文路径例如D:\Desktop\rust环境搭建\HarmonyRustPcAppDevEco/Hvigor 对项目路径有限制中文路径会导致构建失败Invalid project path2.2 安装 Rust在 PowerShell 中检查 Rustrustc--version cargo--version rustup--version如果命令不可用需要把 Rust 默认安装目录加入 PATHC:\Users\你的用户名\.cargo\bin本文示例机器中路径为C:\Users\GZX\.cargo\bin2.3 安装鸿蒙 Rust target鸿蒙 PC 模拟器和真机需要不同 ABI。建议同时安装rustup target add x86_64-unknown-linux-ohos rustup target add aarch64-unknown-linux-ohos如果下载慢可以临时使用国内镜像$env:RUSTUP_DIST_SERVERhttps://mirrors.ustc.edu.cn/rust-staticrustup target add x86_64-unknown-linux-ohos rustup target add aarch64-unknown-linux-ohos验证已安装 targetrustup 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 └── resources3.2 关键文件说明文件作用Index.ets计算器页面和交互状态napi_init.cpp导出 Native 方法给 ArkTSlib.rsRust 计算逻辑Cargo.tomlRust 静态库配置CMakeLists.txt调用 Cargo 并链接 Rust 静态库index.d.ts给 ArkTS 提供 NAPI 类型声明entry/build-profile.json5Native ABI 配置四、配置鸿蒙 Native ABI4.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.soPC 模拟器常见 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 abortNative 场景下减少 unwinding 风险。lto true发布构建启用链接优化。5.2 Rust 运算逻辑Rust 中处理加、减、乘、除并对除零和无效操作符返回明确错误fncalculate(left:f64,right:f64,operator:str)-String{matchoperator{format_number(leftright),-format_number(left-right),*format_number(left*right),/{ifright.abs()f64::EPSILON{Cannot divide by zero.to_string()}else{format_number(left/right)}}_Invalid operator.to_string(),}}结果格式化函数会去掉多余小数尾零fnformat_number(value:f64)-String{if!value.is_finite(){returnError.to_string();}letmuttextformat!({value:.10});whiletext.contains(.)text.ends_with(0){text.pop();}iftext.ends_with(.){text.pop();}iftext-0{return0.to_string();}text}5.3 FFI 导出函数C 无法直接调用 Rust 的普通函数需要通过 C ABI 导出#[no_mangle]pubunsafeexternCfnrust_calculate(left:c_double,right:c_double,operator:*constc_char,)-*mutc_char{ifoperator.is_null(){returninto_c_string(Invalid operator.to_string());}letoperatorCStr::from_ptr(operator).to_string_lossy();into_c_string(calculate(left,right,operator.as_ref()))}Rust 返回的是 C 字符串指针因此还需要提供释放函数#[no_mangle]pubunsafeexternCfnrust_free_c_string(value:*mutc_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 函数externC{char*rust_calculate(doubleleft,doubleright,constchar*operator_text);voidrust_free_c_string(char*value);}6.2 参数转换ArkTS 调用 Native 方法时参数类型是napi_value。需要转换为 C 类型staticdoubleGetDoubleArg(napi_env env,napi_value value){doubleresult0.0;napi_get_value_double(env,value,result);returnresult;}字符串参数转换staticstd::stringGetStringArg(napi_env env,napi_value value){size_t length0;napi_get_value_string_utf8(env,value,nullptr,0,length);std::vectorcharbuffer(length1);napi_get_value_string_utf8(env,value,buffer.data(),buffer.size(),length);returnstd::string(buffer.data(),length);}6.3 导出 calculate 方法Native 方法接收三个参数左操作数、右操作数、运算符。staticnapi_valueCalculate(napi_env env,napi_callback_info info){size_t argc3;napi_value args[3]{nullptr,nullptr,nullptr};napi_get_cb_info(env,info,argc,args,nullptr,nullptr);doubleleftargc0?GetDoubleArg(env,args[0]):0.0;doublerightargc1?GetDoubleArg(env,args[1]):0.0;std::string operatorTextargc2?GetStringArg(env,args[2]):;char*messagerust_calculate(left,right,operatorText.c_str());napi_value resultnullptr;napi_create_string_utf8(env,messagenullptr?Error:message,NAPI_AUTO_LENGTH,result);rust_free_c_string(message);returnresult;}6.4 注册 NAPI 模块staticnapi_valueInit(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);returnexports;}这里的calculate就是 ArkTS 侧最终导入的方法。七、ArkTS 页面实现7.1 声明 Native 类型为了让 ArkTS 识别libentry.so中的函数需要提供类型声明exportconstcalculate:(left:number,right:number,operator:string)string;declareconst_default:{calculate:typeofcalculate;};exportdefault_default;路径为entry/src/main/cpp/types/libentry/index.d.ts7.2 ArkTS 导入 Native 方法页面中使用命名导入import{calculate}fromlibentry.so;不要把 Native 模块当成无类型对象随意调用否则 ArkTS 严格模式可能报Use explicit types instead of any, unknown7.3 页面状态设计计算器页面维护几个核心状态Statedisplay:string0;Stateexpression:string;StatestoredValue:number|nullnull;StatependingOperator:string;StateshouldResetDisplay:booleanfalse;StatehasError:booleanfalse;字段说明状态作用display当前显示内容expression上方表达式提示storedValue已输入的左操作数pendingOperator等待执行的运算符shouldResetDisplay下一次输入是否重置屏幕hasError当前是否为错误状态7.4 调用 Rust 完成计算当用户点击运算符或等号时ArkTS 调用 Nativeconstresult:stringcalculate(this.storedValue,currentValue,this.pendingOperator);如果 Rust 返回错误字符串页面进入错误状态if(result.startsWith(Cannot)||result.startsWith(Invalid)||resultError){this.setError(result);return;}这样 UI 层不需要重复实现除零和无效运算判断核心规则集中在 Rust 中。八、CMake 调用 Cargo 构建 Rust8.1 选择 Rust targetCMake 根据鸿蒙 ABI 选择 Rust targetif(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 查找 cargoDevEco Studio 启动 CMake 时不一定继承系统 PATH因此 CMake 里需要兜底查找 cargofind_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.soadd_library(entry SHARED napi_init.cpp) target_link_libraries(entry PUBLIC rust_core libace_napi.z.so)九、构建与运行9.1 安装依赖在项目根目录执行ohpm install9.2 构建 HAPhvigorw--mode module-p moduleentry assembleHap--stacktrace构建成功会看到BUILD SUCCESSFULHAP 输出位置entry/build/default/outputs/default/entry-default-unsigned.hap9.3 验证 Native so构建成功后应同时存在entry/build/default/intermediates/libs/default/arm64-v8a/libentry.so entry/build/default/intermediates/libs/default/x86_64/libentry.so如果只存在arm64-v8aPC 模拟器安装时很可能失败。9.4 DevEco Studio 运行运行步骤使用 DevEco Studio 打开英文路径项目。等待 Sync 完成。选择 PC/2in1 模拟器。点击 Run。如果提示签名问题启用自动签名。十、常见问题与解决方案10.1 中文路径导致构建失败错误示例Invalid project path解决方式把工程移动到英文路径例如D:\Desktop\HarmonyRustPcApp10.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错误示例cant 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-ohos10.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}fromlibentry.so;constresult:stringcalculate(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 ... ok11.2 HAP 构建验证构建命令hvigorw--mode module-p moduleentry assembleHap--stacktrace验证结果BUILD SUCCESSFUL产物示例entry-default-unsigned.hap11.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 中维护历史记录列表interfaceHistoryItem{expression:string;result:string;}也可以把历史记录持久化到本地存储提升工具完整度。12.3 支持科学计算Rust 层可以继续增加平方。开方。百分比。三角函数。幂运算。取余。这些逻辑放在 Rust 中更容易测试也方便复用到其他平台。十三、工程实践建议13.1 Rust 负责稳定核心适合放进 Rust 的内容包括数学计算。文本解析。加密解密。文件格式处理。协议解析。跨平台核心业务规则。不建议把所有 UI 状态都搬到 Rust。UI 状态通常和 ArkUI 组件生命周期强相关放在 ArkTS 中更自然。13.2 Native 边界要清晰ArkTS 和 Rust 之间不要传递复杂对象。初期建议使用numberstringboolean简单 JSON 字符串复杂数据可以先序列化成 JSON再由 Rust 解析。这样调试成本更低。总结本文完成了一个鸿蒙 PC Rust 计算器项目。项目采用 ArkTS 编写界面C NAPI 负责 Native 桥接Rust 实现核心四则运算并通过 CMake/Cargo 集成进 DevEco Studio 构建流程。关键经验有三点鸿蒙 Native 项目必须重视 ABIPC 模拟器通常需要x86_64。Rust 接入鸿蒙时要同时配置aarch64-unknown-linux-ohos和x86_64-unknown-linux-ohos。DevEco 工程路径建议使用英文路径避免 Hvigor 路径校验失败。这个项目虽然是计算器但已经覆盖了 Rust 接入鸿蒙 PC 应用的核心链路。后续可以在这个基础上扩展科学计算器、表达式解析器、历史记录、主题切换或更复杂的 Rust 算法模块。如果这篇文章对你有帮助欢迎点赞、收藏、关注。你的支持是我持续分享鸿蒙、Rust 和跨平台 Native 开发实践的动力。相关资源Rust 官方网站Rust BookCargo BookOpenHarmony 官方文档HarmonyOS 开发者文档CMake 官方文档