FAQ¶
问题列表¶
我应该在哪里寻求帮助?¶
如果你遇到任何问题,请随时访问讨论页面并开启一个 Q&A 线程以获取帮助。
我应该在哪里使用 Rust?¶
理想情况下,Flutter 负责跨平台用户界面,而 Rust 处理业务逻辑。前端和后端可以完全分离,这意味着 Dart 和 Rust 代码可以彼此解耦。
数据如何在 Dart 和 Rust 之间传递?¶
在 Dart 和 Rust 之间发送的数据本质上是字节数组,在 Dart 中表示为 Uint8List,在 Rust 中表示为 Vec<u8>。你可以发送可序列化的消息和任何你希望的二进制数据,例如高分辨率图像或某种文件数据。
从 Rust crate 生成的库文件在哪里?¶
Rinf 的所有构建设置确保从 Rust crate 编译的所有库文件都被正确包含在最终构建中,随时可以分发。因此你无需担心捆绑库文件的问题。
Android 应用构建失败。我应该怎么办?¶
你项目期望的 NDK 版本在 android/app/build.gradle 文件中指定,位于 android 块内的 ndkVersion 变量。此 ndkVersion 的值应为 flutter.ndkVersion,并且你应使用 Flutter SDK 3.10 或更高版本 。但是,如果你使用 Flutter SDK 3.7 及更早版本创建 Flutter 项目,则 ndkVersion 可能不存在。如果 ndkVersion 未在你的 android/app/build.gradle 文件中定义,请继续并自行写入一个。
..
android {
namespace "com.cunarist.rinf_example"
compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion // <------ 此 行
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
..
并发的底层工作原理是怎样的?¶
在原生平台上,Dart 运行在主线程中,而 Rust 利用异步 tokio 运行时,允许异步任务在单独的线程中高效运行。
在 Web 平台上,Dart 和 Rust 都在主线程中的 JavaScript 异步事件循环内运行,Rust 的 Future 在内部被转换为 JavaScript 的 Promise。这是一个必要的限制,因为截至 2024 年 2 月,WebAssembly 组件webassembly 组件提案尚未稳定。
对 Rust 代码所做的更改会在 Dart 的热重启时生效吗?¶
不会,更新的 Rust 代码无法在 Dart 热重启时加载。要应用更改,应用需要重新编译,因为应用二进制文件必须再次链接到新编译的 Rust 库文件。此限制源于 Rust 编译过程,因为 Rust 本身不支持热重启功能。
在原生平台上,Dart 的热重启会使 Rust 逻辑重新启动,换句话说,就是重新执行 async fn main() 函数。在 Web 平台上,Dart 的热重启对 Rust 逻辑没有影响,因为无法取消已在 JavaScript 事件循环中排队的全部异步任务。
我如何使用 nightly Rust?¶
为了使用 nightly Rust,你需要添加一个 cargokit 配置文件。Cargokit 是本框架使用的 Dart 与 Rust 之间的构建连接器。
cargo:
debug:
toolchain: nightly
release:
toolchain: nightly
关于 cargokit.yaml 的更多信息可以在下面的链接中找到。Cargokit 是用于各种 Flutter 项目(包括 Rinf)中的 Rust crate 的链接器。
https://github.com/irondash/cargokit/blob/main/docs/architecture.md
在 Dart 和 Rust 之间传递机密参数是否足够安全?¶
在 Dart 和 Rust 之间传递机密参数是安全的。其他一些 Rust GUI 框架使用 HTTP 或 WebSocket 在 GUI 和 Rust 之间通信,这相当不安全。但 Rinf 并非如此,因为消息是在 Flutter 应用进程内部传递的。请注意,虽然逆向工程编译后的原生二进制文件以搜索密钥或参数比较困难,但通常不建议在应用本身中硬编码敏感信息。此警告适用于本框架以及任何其他分发的二进制文件。
我可以在纯 Dart 项目中使用它吗?¶
不可以,本框架仅支持 GUI Flutter 应用,因为它本质上是一个 Flutter FFI 插件。本框架不支持其他类型的项目:
Flutter GUI app: 主要支持 ☀️
Flutter plugin: 不支持,但此包可以提供灵感
Dart CLI app: 可能无法工作,本框架必须挂接到 Flutter SDK 上
Dart package: 不支持,无法挂接到 Dart SDK 构建过程
不过,我们也承诺提供最佳的开发体验,因为它主要专注于这一个类别。
当 Rust 中发生 panic 时会发生什么?¶
Rust panic 不会使应用崩溃;它只会取消已生成的异步任务。你无需担心因 Rust panic 导致的应用稳定性问题。当发生 panic 时,如果应用在调试模式下运行,信息将显示在 CLI 中。
在 Web 平台上,不幸的是,由于截至 2024 年 1 月
wasm-bindgen中的此限制,Rust panic 不会沿调用栈向上传播。
如何让 Rust-analyzer 以 WebAssembly 模式进行 lint 检查?¶
由于 Web 平台和原生平台的环境差异很大,有时你需要使用这些属性来根据是否针对 Web 平台来决定代码的包含和排除。
默认情况下,Rust-analyzer 以原生模式运行。要使其以 WebAssembly 模式运行,请创建配置文件:
[build]
# 取消注释下面一行,将 Rust-analyzer 切换为针对 Web 目标
# 以 WebAssembly 模式进行类型检查和 lint 检查。
# 你可能需要重启 Rust-analyzer 才能使此更改生效。
target = "wasm32-unknown-unknown"
你需要重启 Rust 语言服务器才能使此更改生效。
移动应用文件夹后 CMake 缓存损坏¶
CMake Error: The current CMakeCache.txt directory C:/.../CMakeCache.txt is different than the directory C:/... where CMakeCache.txt was created. This may result in binaries being created in the wrong place. If you are not sure, reedit the CMakeCache.txt
CMake Error: The source "C:/.../CMakeLists.txt" does not match the source "C:/.../CMakeLists.txt" used to generate cache. Re-run cmake with a different source directory.
Building Windows application... 80ms
Exception: Unable to generate build files
此错误可以通过以下命令轻松修复。
flutter clean
cargo clean
在较旧的 Android 版本上遇到了与加载原生库相关的错误¶
如果你使用的是较旧的 Android 版本,由于原生库加载的问题,你可能会遇到错误。
要解决此问题,你可以按如下方式修改 android/app/src/ 下的 AndroidManifest.xml 文件。
:caption: android/app/src/**/AndroidManifest.xml
<application
android:extractNativeLibs="true"
>
某些标准库模块在 Web 平台上无法工作¶
截至 2024 年 2 月,Rinf 使用 wasm32-unknown-unknown Rust 目标进行 Web 开发。然而,该目标存在一定的局限性,特别是在系统 IO 能力方面。希望在 WebAssembly 组件提案 稳定后,未来能够过渡到 wasm32-wasi。
以下是 wasm32-unknown-unknown 目标的当前限制:
std::fs中的许多功能尚未实现。std::net的各种功能不可用。请考虑使用reqwestcrate。reqwest支持wasm32-unknown-unknown,并依赖 JavaScript 来执行网络通信。std::thread::spawn无法工作。请考虑使用tokio_with_wasm::task::spawn_blocking代替。std::time::Instant的若干功能尚未实现。请考虑使用chrono作为替代。chrono支持wasm32-unknown-unknown,并依赖 JavaScript 来获取系统时间。如果异步 Rust 任务发生 panic,它会中止并抛出一个 JavaScript
RuntimeError,Rust 无法捕获。推荐的做法是使用Err实例来处理错误。
应用加载动态库失败¶
Exception has occurred.
ArgumentError (Invalid argument(s): Failed to load dynamic library 'libhub.so': dlopen failed: cannot locate symbol "..." referenced by ...
当一个或多个 Rust 依赖项期望将 C 或 C++ 库链接到 hub crate 时,可能会发生这种情况。并非所有 crates.io 上的 Rust crate 都是纯 Rust 编写的,有些依赖于带有 libc++``libstdc++ 等的 C 代码。
为了让 cargo 将这些 C 或 C++ 库链接到你的原生库,请像下面这样创建你的 build.rs 文件。
use std::env;
fn main() {
let target_os = env::var("CARGO_CFG_TARGET_OS");
match target_os.as_ref().map(|x| &**x) {
Ok("android") => {
println!("cargo:rustc-link-lib=dylib=stdc++");
println!("cargo:rustc-link-lib=c++_shared");
},
_ => {}
}
}
上面的代码描述了如何将 libc++ 链接到你的 Android 应用。你可以修改此代码以适应特定场景。
这些链接可能会有所帮助:
https://github.com/cunarist/rinf/issues/280
https://kazlauskas.me/entries/writing-proper-buildrs-scripts
https://github.com/RustAudio/rodio/issues/404
https://github.com/breez/c-breez/issues/553
如何设置编译后的动态库的路径?¶
你可能希望在嵌入式设备上运行应用。然而,在非主流平台上运行应用时,你可能会遇到此错误:
Failed to load dynamic library 'libhub.so': libhub.so: cannot open shared object file: No such file or directory
在这种情况下,你可以指定一个指向已编译 Rust 库的路径。只需向你的动态库文件提供一个字符串路径即可。
import 'src/bindings/bindings.dart';
async void main() {
await initializeRust(compiledLibPath: 'path/to/library/libhub.so');
}
此提供的路径将用于在原生平台上通过 Dart 的 DynamicLibrary.open([compiledLibPath]) 查找动态库文件,并在 Web 上通过 import init, _ as wasmBindings from "[compiledLibPath]" 加载 JavaScript 模块。