您现在的位置是:网站首页> PY&Rust
Rust学习及开源软件库资料
- PY&Rust
- 2026-11-05
- 2756人已阅读
Rust学习及开源软件库资料
****带虚拟环境的main.py如何用批处理来运行他,copy到其他机器直接可运行的****
vscode的配置文件在-》C:\Users\xn\AppData\Roaming\Code\User-》settings.json

***Rust在线学习资料***(最好书-》Rust权威指南)
开源软件库
基于Rust开发的m3u8下载器M3U8Quicker:支持断点续传、边下边播
Rust下载安装
1. 安装Rust环境
rust默认安装路径
1.Windows 系统
使用 rustup 安装时,默认路径通常为:
C:\Users\<用户名>\.cargo\
其中包含:
bin\:存放 rustc、cargo 等可执行文件
registry\: crate 注册表缓存
toolchains\:不同版本的 Rust 工具链
2.Linux 或 macOS 系统
默认安装路径为用户主目录下的隐藏文件夹:
~/.cargo/(即 /home/<用户名>/.cargo/ 或 /Users/<用户名>/.cargo/)
结构与 Windows 类似,bin 目录包含可执行文件。
***可在在windows的环境变量里面设置RUSTUP_HOME 以及 CARGO_HOME 通过设置这两个环境变量来设置默认安装路径。
环境变量:
变量名 值
CARGO_HOME cargo 的安装路径(必须为绝对路径),例如D:\Rust\Cargo
RUSTUP_HOME rustup 的安装路径(必须为绝对路径),例如 D:\Rust\Rustup
RUSTUP_DIST_SERVER https://mirrors.ustc.edu.cn/rust-static
RUSTUP_UPDATE_ROOT https://mirrors.ustc.edu.cn/rust-static/rustup
RUST_BACKTRACEfull
RUST 某toolchain的目录,如%RUSTUP_HOME%\toolchains\stable-x86_64-pc-windows-gnu。
RUST_SRC_PATH rust的源码目录,如%RUST%\lib\rustlib\src\rust\src,若你的rustlib中没有src,请执行> rustup component add rust-src。
RUSTBINPATH %CARGO_PATH%\bin。
卸载 Rust
在任何时候如果您想卸载 Rust,您可以运行 rustup self uninstall。但我们会想念您的!
打开rustup-init.exe文件,若出现以下提示,按"Y"、回车

随后出现以下选项

如已经安装了vs2022直接选1就可以不用2手动安装
依次输入"2"、回车、"x86_64-pc-windows-gnu"、回车、"nightly"、回车、"y"、回车,随后选项更新为

随后输入"1"、回车即可开始安装最新版,等待一定时间后提示如下即表示Rust已经正常安装。

截至目前,你已安装了工具链管理工具rustup、项目构建工具cargo、Rust编译环境、Rust标准库文档
2. 配置开发工具
rustup component add rust-src rustc-dev llvm-tools-preview
1. 执行命令"cargo install racer"
2. 执行命令"cargo install rustfmt"
3. 执行命令"rustup component add rls"
4. 执行命令"rustup component add rust-analysis"
5. 执行命令"rustup component add rust-src"
6. 设置名为"RUST_SRC_HOME"的环境变量为"C:\Users\[home_dir_name]\.multirust\toolchains\nightly-x86_64-pc-windows-gnu\lib\rustlib\src\rust\src"
其中"home_dir_name"为你的家目录的目录名
7. 安装VScode编辑器
8. 安装名为rust-analyzer 的插件并重新加载后用VScode打开任意扩展名为rs的文件
9. 跟随插件引导完成配置,共有四项需要配置,其中两项用于选择rustup的工具链,由于之前的内容中只安装了nightly版本的工具链,故均只可选择nightly;另外两项一个是选择补全所用工具,选择rls即可,随后要求选择是否使用Rustfmt,选择使用即可
所有选项均可在用户设置中找到。

截至目前,理论上开发环境已经完成,且为模拟新手第一次安装的情况,我先卸载了rust并清除了相关配置后重新安装、配置了一遍,但由于可能因遗漏而未清除的配置,我的起步环境可能与你的不同,欢迎指正。
在终端中输入命令:
cargo new greeting
当前文件下下会构建一个名叫 greeting 的 Rust 工程目录
调试需要安装插件Native Debug和C/C++扩展
调试文件launch.json
重要目录
c:\Users\[用户名]\.cargo
c:\Users\[用户名]\.rustup
调试过程中,当要跟踪到rust内部代码时,会跳出如下提示框,这时需要建一个C:\rustc\b8cedc00407a4c56a3bda1ed605c6fc166655447的目录,并将用户目录下的.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\src拷到前面的目录下。这样就能跟踪到内部代码了。

Cargo创建项目编译项目等使用说明
在 Rust 中,可以使用 Cargo 工具来编译生成可执行文件。Cargo 是 Rust 的构建系统和包管理器,它简化了 Rust 项目的构建、测试和部署过程。
要使用 Cargo 编译生成可执行文件,可以按照以下步骤进行:
确保已经安装了 Rust 和 Cargo。可以从 Rust 官方网站(https://www.rust-lang.org/)下载并安装。
打开终端或命令行界面,导航到你的 Rust 项目目录。
如果你还没有创建 Rust 项目,可以使用以下命令创建一个新的二进制项目:
cargo new my_project
这将创建一个名为 "my_project" 的新目录,其中包含 Cargo.toml 配置文件和 src 目录。
进入项目目录:
cd my_project
编写你的 Rust 代码,并将其保存在 src 目录下的 main.rs 文件中。
使用以下命令编译项目:
cargo build --release
这将在项目目录下的 target/release 目录中生成优化后的可执行文件。
运行生成的可执行文件:
在 Linux/macOS 上:
./target/release/my_project
在 Windows 上:
.\target\release\my_project.exe
注意:将 "my_project" 替换为你实际的项目名称。
Cargo 还提供了其他有用的命令:
cargo run:编译并运行项目,适合开发和测试阶段。
cargo test:运行项目的单元测试。
cargo doc:生成项目的文档。
cargo clean:清理项目的构建结果。
使用 Cargo 可以大大简化 Rust 项目的管理和构建过程。它会自动处理依赖项的下载和编译,并提供了一致的项目结构和构建命令。
Rust的包、模块、结构体和特征(接口)的概念
包(Crate)和模块(Module)的区别:
包(Crate):
包是Rust中最高级别的代码组织单位。
一个包可以包含多个模块。
包可以是二进制包(可执行程序)或库包(供其他程序使用的代码库)。
每个包都有一个Cargo.toml文件,用于描述包的元数据和依赖关系。
模块(Module):
模块是在包内组织代码的方式。
模块可以包含函数、结构体、特征、常量等。
模块可以嵌套,形成层次结构。
模块用于控制项的可见性(公有或私有)。
示例:
// 这是一个名为 "my_package" 的包
// lib.rs (库包的根模块)
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
pub fn eat_at_restaurant() {
front_of_house::hosting::add_to_waitlist();
}
// 在同一个包中的另一个文件 (比如 src/back_of_house.rs)
pub struct Breakfast {
pub toast: String,
seasonal_fruit: String,
}
impl Breakfast {
pub fn summer(toast: &str) -> Breakfast {
Breakfast {
toast: String::from(toast),
seasonal_fruit: String::from("peaches"),
}
}
}
结构体(Struct)和特征(Trait):
结构体(Struct):
结构体是自定义数据类型,用于组合多个相关的值。
可以包含多个命名字段。
可以实现方法。
特征(Trait):
特征定义了一组方法签名,可以被其他类型实现。
类似于其他语言中的接口概念。
允许抽象和多态。
示例:
// 定义一个结构体
struct Rectangle {
width: u32,
height: u32,
}
// 为结构体实现方法
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
// 定义一个特征
trait Drawable {
fn draw(&self);
}
// 为Rectangle实现Drawable特征
impl Drawable for Rectangle {
fn draw(&self) {
println!("Drawing a rectangle of {}x{}", self.width, self.height);
}
}
// 另一个实现Drawable特征的结构体
struct Circle {
radius: f64,
}
impl Drawable for Circle {
fn draw(&self) {
println!("Drawing a circle with radius {}", self.radius);
}
}
// 使用特征作为参数
fn draw_shape(shape: &impl Drawable) {
shape.draw();
}
fn main() {
let rect = Rectangle { width: 30, height: 50 };
let circ = Circle { radius: 10.0 };
draw_shape(&rect);
draw_shape(&circ);
// 调用 area 方法
let area1 = rect .area();
println!("The area of the rectangle is {} square pixels.", area1);
}
在这个例子中:
Rectangle和Circle是结构体。
Drawable是一个特征,定义了draw方法。
两个结构体都实现了Drawable特征。
draw_shape函数可以接受任何实现了Drawable特征的类型。
Rust没有传统意义上的类,但结构体和特征的组合提供了类似的功能:
结构体用于数据封装
impl块用于定义方法
特征用于定义接口和实现多态
这种设计提供了更大的灵活性和更清晰的代码组织
Rust学习笔记
Rust中的关键字
严格关键字
as - 强制类型转换,消除特定包含项的 trait 的歧义,或者对 use 和 extern crate 语句中的项重命名
async - 返回一个 Future 而不是阻塞当前线程( 2018版新增)
await - 暂停执行直到 Future 的结果就绪( 2018版新增)
break - 立刻退出循环
const - 定义常量或不变裸指针(constant raw pointer)
continue - 继续进入下一次循环迭代
crate - 链接(link)一个外部 crate 或一个代表宏定义的 crate 的宏变量
dyn - 动态分发 trait 对象
else - 作为 if 和 if let 控制流结构的 fallback
enum - 定义一个枚举
extern - 链接一个外部 crate 、函数或变量
false - 布尔字面值 false
fn - 定义一个函数或 函数指针类型 (function pointer type)
for - 遍历一个迭代器或实现一个 trait 或者指定一个更高级的生命周期
if - 基于条件表达式的结果分支
impl - 实现自有或 trait 功能
in - for - 循环语法的一部分
let - 绑定一个变量
loop - 无条件循环
match - 模式匹配
mod - 定义一个模块
move - 使闭包获取其所捕获项的所有权
mut - 表示引用、裸指针或模式绑定的可变性
pub - 表示结构体字段、impl 块或模块的公有可见性
ref - 通过引用绑定
return - 从函数中返回
Self - 定义或实现 trait 的类型的类型别名
self - 表示方法本身或当前模块
static - 表示全局变量或在整个程序执行期间保持其生命周期
struct - 定义一个结构体
super - 表示当前模块的父模块
trait - 定义一个 trait
true - 布尔字面值 true
type - 定义一个类型别名或关联类型
union - 定义一个 union 并且是 union 声明中唯一用到的关键字
use - 引入外部空间的符号
where - 表示一个约束类型的从句
while - 基于一个表达式的结果判断是否进行循环
保留关键字
以下关键字目前没有任何功能,不过由 Rust 保留以备将来使用
abstract
become
box
do
final
macro
override
priv
try
typeof
unsized
virtual
yield
弱关键字
unsafe - 表示不安全的代码、函数、trait 或实现
static
函数中途返回使用return
fn check_number(x: i32) -> bool {
if x == 0 {
return true;
}
false
}
fn main() {
let result = check_number(0);
println!("result: {}", result); // 输出:result: true
}
数据类型
整数型(Integer)
整数型简称整型,按照比特位长度和有无符号分为以下种类:
位长度 有符号 无符号
8-bit i8 u8
16-bit i16 u16
32-bit i32 u32
64-bit i64 u64
128-bit i128 u128
arch isize usize
isize 和 usize 两种整数类型是用来衡量数据大小的,它们的位长度取决于所运行的目标平台,如果是 32 位架构的处理器将使用 32 位位长度整型。
浮点数型(Floating-Point)
Rust 与其它语言一样支持 32 位浮点数(f32)和 64 位浮点数(f64)。默认情况下,64.0 将表示 64 位浮点数,因为现代计算机处理器对两种浮点数计算的速度几乎相同,但 64 位浮点数精度更高。
实例
fn main() {
let x = 2.0; // f64
let y: f32 = 3.0; // f32
}
布尔型
布尔型用 bool 表示,值只能为 true 或 false。
字符型
字符型用 char 表示。
Rust的 char 类型大小为 4 个字节
复合类型
元组是用一对 ( ) 包括的一组数据,可以包含不同种类的数据:
实例
let tup: (i32, f64, u8) = (500, 6.4, 1);
// tup.0 等于 500
// tup.1 等于 6.4
// tup.2 等于 1
let (x, y, z) = tup;
// y 等于 6.4
数组用一对 [ ] 包括的同类型数据。
实例
let a = [1, 2, 3, 4, 5];
// a 是一个长度为 5 的整型数组
let b = ["January", "February", "March"];
// b 是一个长度为 3 的字符串数组
let c: [i32; 5] = [1, 2, 3, 4, 5];
// c 是一个长度为 5 的 i32 数组
let d = [3; 5];
// 等同于 let d = [3, 3, 3, 3, 3];
let first = a[0];
let second = a[1];
// 数组访问
a[0] = 123; // 错误:数组 a 不可变
let mut a = [1, 2, 3];
a[0] = 4; // 正确
闭包的声明
闭包的语法声明:
|参数...| { 表达式 }
闭包的参数和返回值: 闭包可以有零个或多个参数,并且可以返回一个值。
let calculate = |a, b, c| a * b + c;
闭包的调用:闭包可以像函数一样被调用。
let result = calculate(1, 2, 3);
结构体定义
这是一个结构体定义:
struct Site {
domain: String,
name: String,
nation: String,
found: u32
}
构体实例
Rust 很多地方受 JavaScript 影响,在实例化结构体的时候用 JSON 对象的 key: value 语法来实现定义:
实例
let runoob = Site {
domain: String::from("www.runoob.com"),
name: String::from("RUNOOB"),
nation: String::from("China"),
found: 2013
};
如果你不了解 JSON 对象,你可以不用管它,记住格式就可以了:
结构体类名 {
字段名 : 字段值,
...
}
这样的好处是不仅使程序更加直观,还不需要按照定义的顺序来输入成员的值。
如果正在实例化的结构体有字段名称和现存变量名称一样的,可以简化书写:
实例
let domain = String::from("www.runoob.com");
let name = String::from("RUNOOB");
let runoob = Site {
domain, // 等同于 domain : domain,
name, // 等同于 name : name,
nation: String::from("China"),
traffic: 2013
};
有这样一种情况:你想要新建一个结构体的实例,其中大部分属性需要被设置成与现存的一个结构体属性一样,仅需更改其中的一两个字段的值,可以使用结构体更新语法:
let site = Site {
domain: String::from("www.runoob.com"),
name: String::from("RUNOOB"),
..runoob
};
注意:..runoob 后面不可以有逗号。这种语法不允许一成不变的复制另一个结构体实例,意思就是说至少重新设定一个字段的值才能引用其他实例的值。
元组结构体
有一种更简单的定义和使用结构体的方式:元组结构体。
元组结构体是一种形式是元组的结构体。
与元组的区别是它有名字和固定的类型格式。它存在的意义是为了处理那些需要定义类型(经常使用)又不想太复杂的简单数据:
struct Color(u8, u8, u8);
struct Point(f64, f64);
let black = Color(0, 0, 0);
let origin = Point(0.0, 0.0);
实例
fn main() {
struct Color(u8, u8, u8);
struct Point(f64, f64);
let black = Color(0, 0, 0);
let origin = Point(0.0, 0.0);
println!("black = ({}, {}, {})", black.0, black.1, black.2);
println!("origin = ({}, {})", origin.0, origin.1);
}
结构体方法
方法(Method)和函数(Function)类似,只不过它是用来操作结构体实例的。
实例
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!("rect1's area is {}", rect1.area());
}
请注意,在调用结构体方法的时候不需要填写 self ,这是出于对使用方便性的考虑。
一个多参数的例子:
实例
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
fn wider(&self, rect: &Rectangle) -> bool {
self.width > rect.width
}
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
let rect2 = Rectangle { width: 40, height: 20 };
println!("{}", rect1.wider(&rect2));
}
结构体关联函数
之所以"结构体方法"不叫"结构体函数"是因为"函数"这个名字留给了这种函数:它在 impl 块中却没有 &self 参数。
这种函数不依赖实例,但是使用它需要声明是在哪个 impl 块中的。
一直使用的 String::from 函数就是一个"关联函数"。
实例
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn create(width: u32, height: u32) -> Rectangle {
Rectangle { width, height }
}
}
fn main() {
let rect = Rectangle::create(30, 50);
println!("{:?}", rect);
}
Rust 中的mod 使用
项目目录如下图。
2.1、mod.rs中是需要引入的模块代码。
2.2、main.rs和文件夹utils在src文件夹下。

2.3、mod.rs代码如下。
pub mod nation{
pub mod government{
pub fn govern()
{
let a=String::from("govern");
println!("This is {}",a);
}
}
mod congress{
pub fn legislate()
{
let a=String::from("legislate");
println!("This is {}",a);
}
}
pub mod court{
pub fn judicial()
{
super::congress::legislate();
}
}
}
3、main.rs代码如下。
mod utils;
fn main() {
utils::nation::government::govern();
utils::nation::court::judicial();
}
4、缩短模块引用路径方法如下。
mod utils;
use utils::nation::court::judicial;
use utils::nation::government::govern;
fn main() {
govern();
judicial()
}
5、运行结果如下图。

Rust 枚举类
枚举类在 Rust 中并不像其他编程语言中的概念那样简单,但依然可以十分简单的使用:
实例
#[derive(Debug)]
enum Book {
Papery, Electronic
}
fn main() {
let book = Book::Papery;
println!("{:?}", book);
}
如果你现在正在开发一个图书管理系统,你需要描述两种书的不同属性(纸质书有索书号,电子书只有 URL),你可以为枚举类成员添加元组属性描述:
enum Book {
Papery(u32),
Electronic(String),
}
let book = Book::Papery(1001);
let ebook = Book::Electronic(String::from("url://..."));
如果你想为属性命名,可以用结构体语法:
enum Book {
Papery { index: u32 },
Electronic { url: String },
}
let book = Book::Papery{index: 1001};
Rust 中有三个重要的组织概念:箱、包、模块。
箱(Crate)
"箱"是二进制程序文件或者库文件,存在于"包"中。
"箱"是树状结构的,它的树根是编译器开始运行时编译的源文件所编译的程序。
注意:"二进制程序文件"不一定是"二进制可执行文件",只能确定是是包含目标机器语言的文件,文件格式随编译环境的不同而不同。
包(Package)
当我们使用 Cargo 执行 new 命令创建 Rust 工程时,工程目录下会建立一个 Cargo.toml 文件。工程的实质就是一个包,包必须由一个 Cargo.toml 文件来管理,该文件描述了包的基本信息以及依赖项。
一个包最多包含一个库"箱",可以包含任意数量的二进制"箱",但是至少包含一个"箱"(不管是库还是二进制"箱")。
当使用 cargo new 命令创建完包之后,src 目录下会生成一个 main.rs 源文件,Cargo 默认这个文件为二进制箱的根,编译之后的二进制箱将与包名相同。
模块(Module)
对于一个软件工程来说,我们往往按照所使用的编程语言的组织规范来进行组织,组织模块的主要结构往往是树。Java 组织功能模块的主要单位是类,而 JavaScript 组织模块的主要方式是 function。
这些先进的语言的组织单位可以层层包含,就像文件系统的目录结构一样。Rust 中的组织单位是模块(Module)
mod nation {
mod government {
fn govern() {}
}
mod congress {
fn legislate() {}
}
mod court {
fn judicial() {}
}
}
访问权限
Rust 中有两种简单的访问权:公共(public)和私有(private)。
默认情况下,如果不加修饰符,模块中的成员访问权将是私有的。
如果想使用公共权限,需要使用 pub 关键字。
对于私有的模块,只有在与其平级的位置或下级的位置才能访问,不能从其外部访问。
实例
mod nation {
pub mod government {
pub fn govern() {}
}
mod congress {
pub fn legislate() {}
}
mod court {
fn judicial() {
super::congress::legislate();
}
}
}
fn main() {
nation::government::govern();
}
mod back_of_house {
pub struct Breakfast {
pub toast: String,
seasonal_fruit: String,
}
impl Breakfast {
pub fn summer(toast: &str) -> Breakfast {
Breakfast {
toast: String::from(toast),
seasonal_fruit: String::from("peaches"),
}
}
}
}
pub fn eat_at_restaurant() {
let mut meal = back_of_house::Breakfast::summer("Rye");
meal.toast = String::from("Wheat");
println!("I'd like {} toast please", meal.toast);
}
fn main() {
eat_at_restaurant()
}
use 关键字
use 关键字能够将模块标识符引入当前作用域:
实例
mod nation {
pub mod government {
pub fn govern() {}
}
}
use crate::nation::government::govern;
fn main() {
govern();
}
因为 use 关键字把 govern 标识符导入到了当前的模块下,可以直接使用。
这样就解决了局部模块路径过长的问题。
当然,有些情况下存在两个相同的名称,且同样需要导入,我们可以使用 as 关键字为标识符添加别名:
实例
mod nation {
pub mod government {
pub fn govern() {}
}
pub fn govern() {}
}
use crate::nation::government::govern;
use crate::nation::govern as nation_govern;
fn main() {
nation_govern();
govern();
}
Rust 面向对象
下面建造一个完整的类:
second.rs
pub struct ClassName {
field: i32,
}
impl ClassName {
pub fn new(value: i32) -> ClassName {
ClassName {
field: value
}
}
pub fn public_method(&self) {
println!("from public method");
self.private_method();
}
fn private_method(&self) {
println!("from private method");
}
}
main.rs
mod second;
use second::ClassName;
fn main() {
let object = ClassName::new(1024);
object.public_method();
}
Rust 并发编程
Rust 中通过 std::thread::spawn 函数创建新线程:
实例
use std::thread;
use std::time::Duration;
fn spawn_function() {
for i in 0..5 {
println!("spawned thread print {}", i);
thread::sleep(Duration::from_millis(1));
}
}
fn main() {
thread::spawn(spawn_function);
for i in 0..3 {
println!("main thread print {}", i);
thread::sleep(Duration::from_millis(1));
}
}
在 Rust 中,impl 块用于为类型实现功能,而 for 关键字用于指定 impl 块所针对的具体类型。
语法:
impl <Trait> for <Type> {
// Trait 方法的实现
}
<Trait> 是要实现的 trait 的名称。
<Type> 是要为其实现 trait 的类型的名称。
作用:
for 关键字将 impl 块与特定的类型关联起来,使得该类型能够使用 trait 中定义的方法。
示例:
struct Circle {
radius: f64,
}
trait Area {
fn area(&self) -> f64;
}
// 为 Circle 类型实现 Area trait
impl Area for Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * self.radius * self.radius
}
}
fn main() {
let circle = Circle { radius: 5.0 };
println!("Circle area: {}", circle.area()); // 使用 Area trait 中的 area() 方法
}
在这个例子中:
我们定义了一个 Circle 结构体和一个 Area trait。
Area trait 中定义了一个 area() 方法,用于计算面积。
我们使用 impl Area for Circle 语句为 Circle 类型实现了 Area trait。
在 impl 块中,我们实现了 area() 方法,用于计算圆形的面积。
最后,在 main() 函数中,我们创建了一个 Circle 实例,并调用了 area() 方法来计算圆形的面积。
总结:
for 关键字在 impl 块中用于指定要为其实现 trait 的具体类型,从而将 trait 的功能应用于该类型。
2022年末 Rust权威编程语言入门教程
最新最全Rust编程语言入门教程
Rust编程语言入门教程
Rust UI库
rust ui框架slint tauri bevy egui
在 Rust 生态系统中,有多个流行的 UI 库可供选择。以下是一些使用较为广泛的 UI 库:
slint:用 Rust 编写的声明式开源 GUI 工具包,可用于为桌面和嵌入式设备等构建原生用户界面
egui: 一个简单、快速、可移植的即时模式 GUI 库。它使用 Rust 编写,可以轻松地创建跨平台的用户界面。
druid: 由 Rust 团队成员开发的一个现代化、跨平台的原生 GUI 库。它强调简单性、可组合性和高性能。
iced: 一个跨平台的 GUI 库,专注于简单性和类型安全。它借鉴了 Elm 的思想,提供了声明式的 API 和响应式的编程模型。
gtk-rs: Rust 绑定的 GTK 库,可以使用 Rust 编写 GTK 应用程序。GTK 是一个成熟、跨平台的 GUI 工具包。
Tauri: 一个使用 Rust 和 Web 技术构建跨平台桌面应用的框架。它允许使用 HTML、CSS 和 JavaScript 创建用户界面,同时利用 Rust 的性能和安全性。
Azul: 一个用 Rust 编写的跨平台 GUI 框架,旨在提供一致的开发体验和原生的性能。它使用即时模式渲染,支持 CSS 样式和 SVG。
这些只是 Rust 中一部分流行的 UI 库,还有其他一些库也在不断发展和完善中,如 Moxie、Relm 等。选择哪个 UI 库取决于你的具体需求、目标平台以及个人偏好。建议你研究和对比几个库,选择最适合你项目的那个。
**直接用 Rust 代码画界面、无 WebView** → 最多人用:egui
**Rust 做桌面软件的方案** → 工程落地最多:Tauri
## 快速选型建议
- 写小工具、调试面板、游戏编辑器、快速原型:egui
- 写正式桌面软件,想要干净声明式状态:iced
- 工业界面、嵌入式、想快速画出好看界面:slint
- 团队会前端、想要网页生态:Tauri
- 喜欢 React 写法:Dioxus
Slint 的例子
fn main() {
MainWindow::new().unwrap().run().unwrap();
}
slint::slint! {
export component MainWindow inherits Window {
Text {
text: "hello world";
color: green;
}
}
}
一个使用 egui 创建简单计数器应用的例子:
use egui::{CentralPanel, CtxRef, Labelable, Ui};
struct MyApp {
counter: i32,
}
impl Default for MyApp {
fn default() -> Self {
Self { counter: 0 }
}
}
impl MyApp {
fn new(cc: &eframe::CreationContext<'_>) -> Self {
Default::default()
}
}
impl eframe::App for MyApp {
fn update(&mut self, ctx: &egui::CtxRef, _frame: &mut eframe::Frame) {
CentralPanel::default().show(ctx, |ui| {
ui.heading("Counter App");
ui.horizontal(|ui| {
if ui.button("-").clicked() {
self.counter -= 1;
}
ui.label(self.counter.to_string());
if ui.button("+").clicked() {
self.counter += 1;
}
});
});
}
}
fn main() {
let app = MyApp::default();
let native_options = eframe::NativeOptions::default();
eframe::run_native(Box::new(app), native_options);
}
这个例子创建了一个简单的计数器应用:
我们定义了一个 MyApp 结构体,包含一个 counter 字段用于存储当前计数值。
实现了 Default trait,提供了 MyApp 的默认值。
实现了 eframe::App trait 的 update 方法,定义了应用的 UI 布局和交互逻辑。
在 update 方法中,我们使用 CentralPanel 创建了一个居中的面板,并在其中添加了标题、减号按钮、计数值标签和加号按钮。
当减号按钮被点击时,计数值减 1;当加号按钮被点击时,计数值加 1。
在 main 函数中,我们创建了 MyApp 的实例,并使用 eframe::run_native 启动应用。
运行这个例子,你将看到一个居中的计数器应用,可以通过点击加号和减号按钮来增减计数值。
这只是一个简单的示例,egui 还提供了许多其他的 UI 组件和布局方式,可以用来创建更复杂的用户界面。你可以参考 egui 的文档和示例来进一步了解它的功能和用法。
Tauri 的一些例子
文件操作示例
文件对话框 :打开文件对话框并获取用户选择的文件路径。
const { dialog } = require('tauri/api');
dialog.open({ directory: false, multiple: false })
.then((result) => {
console.log('选中的文件路径:', result);
})
.catch((error) => {
console.error('打开文件对话框时出错:', error);
});
读写文件 :使用 Tauri API 读写本地文件。
const { readTextFile, writeTextFile } = require('@tauri-apps/api/fs');
// 读取文件内容
async function readFileContent(filePath) {
try {
const content = await readTextFile(filePath);
console.log('File content:', content);
} catch (error) {
console.error('Error reading file:', error);
}
}
// 写入文件内容
async function writeFileContent(filePath, data) {
try {
await writeTextFile(filePath, data);
console.log('File written successfully!');
} catch (error) {
console.error('Error writing file:', error);
}
}
系统通知示例
使用 Tauri API 发送系统通知。
const { Notification } = require('@tauri-apps/api/notification');
// 发送通知
function sendNotification(title, message) {
const notification = new Notification({ title, body: message });
notification.show();
}
窗口管理示例
监听窗口关闭事件,并在关闭前执行相关操作。
import { appWindow } from '@tauri-apps/api/window'
appWindow.listen('tauri://window-close-requested', () => {
// 窗口关闭前需要执行的任务...
await appWindow.close()
})
前后端通信示例
前端调用 Rust :在 Rust 中定义指令,在前端通过 invoke 函数调用指令。
// Rust 中的指令
#[tauri::command]
fn greet(name: &str) -> String {
format!("👋 Hello, {}!", name)
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet]) // 注册指令
.run(tauri::generate_context!())
.expect("❌ error while running tauri application.")
}
// 前端调用
import { invoke } from '@tauri-apps/api'
invoke('greet', { name: '🚥 红绿灯的黄' })
.then(response => {
window.header.innerHTML = response
})
事件通信 :通过事件进行前后端通信。
// 前端发送事件
import { emit, listen } from '@tauri-apps/api/event'
const greetMsg = ref("");
const name = ref("");
function greet() {
emit("greet", { name: name.value });
}
onMounted(async () => {
const unlisten = await listen('hello', (event) => {
greetMsg.value = event.payload;
});
})
// Rust 中监听事件并处理
use serde::{Deserialize, Serialize, Debug};
use tauri::Manager;
#[derive(Serialize, Deserialize, Debug)]
struct Greet {
name: String,
}
fn main() {
tauri::Builder::default()
.setup(|app| {
let callback_app_handle = app.app_handle().clone();
let _event_id = app.listen_global("greet", move |event| {
let greet_msg: Greet = serde_json::from_str(&event.payload().unwrap()).unwrap();
let msg = format!("Hello, {}! You've been greeted from Rust!", greet_msg.name);
println!("{}", msg);
let _ = &callback_app_handle.emit_all("hello", msg);
});
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
这些示例展示了 Tauri 的一些基本功能和用法,通过这些示例,开发者可以快速上手并构建跨平台的桌面应用。
Rust UI组件GPUI Component
基于Rust开发的m3u8下载器M3U8Quicker:支持断点续传、边下边播
源码地址:https://github.com/Liubsyy/M3U8
特性
支持 M3U8 视频下载与任务管理
支持多线程下载,提升下载效率
支持 AES-128 / AES-192 / AES-256 加密流的密钥拉取与解密
支持暂停、继续下载、失败分片重试
支持边下边播,下载过程中也能直接打开播放器
支持自动合并 ts 并转成 mp4
支持设置代理
支持Chrome自动识别m3u8地址一键跳转新建下载任务
从新建下载任务到播放,一条链路打通
使用 M3U8 Quicker 的过程非常直接。打开应用后,点击“新建下载”,输入 M3U8 地址即可创建任务。如果资源依赖 referer、origin 等附加请求头,也可以在创建任务时一并补充。
Rust 使用 `let` 声明变量。
**默认:不可变 (immutable)**,一旦绑定值,不能修改。
fn main() {
let a = 10;
// a = 20; // 编译报错!a是不可变变量,不能赋值修改
println!("{}", a);
}
想要可变,必须显式加 `mut`:
fn main() {
let mut b = 10;
b = 20; // ok,mut修饰允许修改
println!("{}", b);
}
类型推断 + 显式标注
Rust 绝大多数场景自动推断类型;也可以手动写类型,语法 `变量: 类型`
let x: i32 = 100; // 显式标注i32 32位有符号整数
let y = 200; // 推断i32
let z: f64 = 3.14;
## 变量遮蔽(Shadowing)⭐高频考点
**可以用同名的 `let` 重新声明一个新变量,覆盖旧的。不是修改,是产生新绑定。**
遮蔽和 `mut` 完全不一样。
fn main() {
let x = 5;
let x = x + 1; // 遮蔽,新x,旧x失效
let x = x * 2;
println!("{}", x); // 12
}
## 变量与所有权(Rust 最核心)
> `let` 不只是存值,同时发生所有权绑定。
### 移动语义 move
let s1 = String::from("abc");
let s2 = s1; // s1所有权移动给s2,s1失效
// println!("{}", s1); // 编译报错!s1已经失去所有权
println!("{}", s2);
简单类型(整数、bool、char、f64,实现了`Copy`trait)是拷贝,不会 move:
let a = 10;
let b = a; // copy,a仍然有效
println!("{} {}", a, b);
> 总结:
> - 栈上简单类型:Copy,赋值复制一份;
> - 堆分配类型 (String, Vec):Move,赋值转移所有权,旧变量失效。
### 引用 &,借用,不转移所有权
let s = String::from("test");
let r = &s; // 借用,r是引用,s保持所有权
println!("{} {}", s, r);
## 解构赋值
let 支持元组解构:
let (a, b) = (10, 20);
println!("{} {}", a, b);
## 下划线变量 `_`
`_` 是特殊名字,表示丢弃值,**不会绑定变量,不会接管所有权**
let _ = String::from("hello"); // 直接丢弃,不保存
let x = _; // 非法,_不是变量
快速对比示例,把 mut /shadow/move 放一起
fn main() {
// mut 修改同一个绑定
let mut n = 1;
n = 2;
println!("mut n={}", n);
// shadow 遮蔽生成新变量
let n = n + 1;
println!("shadow n={}", n);
// move所有权
let s1 = String::from("rust");
let s2 = s1;
// println!("{}", s1); // move之后s1报废
println!("s2={}", s2);
}
整数型


浮点数型(Floating-Point)
Rust 与其它语言一样支持 32 位浮点数(f32)和 64 位浮点数(f64)。默认情况下,64.0 将表示 64 位浮点数,因为现代计算机处理器对两种浮点数计算的速度几乎相同,但 64 位浮点数精度更高。
实例
fn main() {
let x = 2.0; // f64
let y: f32 = 3.0; // f32
}
布尔型
布尔型用 bool 表示,值只能为 true 或 false。
字符型
字符型用 char 表示。
Rust的 char 类型大小为 4 个字节,代表 Unicode标量值,这意味着它可以支持中文,日文和韩文字符等非英文字符甚至表情符号和零宽度空格在 Rust 中都是有效的 char 值。
Unicode 值的范围从 U+0000 到 U+D7FF 和 U+E000 到 U+10FFFF (包括两端)。 但是,"字符"这个概念并不存在于 Unicode 中,因此您对"字符"是什么的直觉可能与Rust中的字符概念不匹配。所以一般推荐使用字符串储存 UTF-8 文字(非英文字符尽可能地出现在字符串中)。
注意:由于中文文字编码有两种(GBK 和 UTF-8),所以编程中使用中文字符串有可能导致乱码的出现,这是因为源程序与命令行的文字编码不一致,所以在 Rust 中字符串和字符都必须使用 UTF-8 编码,否则编译器会报错。
复合类型
元组是用一对 ( ) 包括的一组数据,可以包含不同种类的数据:
实例
let tup: (i32, f64, u8) = (500, 6.4, 1);
// tup.0 等于 500
// tup.1 等于 6.4
// tup.2 等于 1
let (x, y, z) = tup;
// y 等于 6.4
数组用一对 [ ] 包括的同类型数据。
实例
let a = [1, 2, 3, 4, 5];
// a 是一个长度为 5 的整型数组
let b = ["January", "February", "March"];
// b 是一个长度为 3 的字符串数组
let c: [i32; 5] = [1, 2, 3, 4, 5];
// c 是一个长度为 5 的 i32 数组
let d = [3; 5];
// 等同于 let d = [3, 3, 3, 3, 3];
let first = a[0];
let second = a[1];
// 数组访问
a[0] = 123; // 错误:数组 a 不可变
let mut a = [1, 2, 3];
a[0] = 4; // 正确
结构体 (Structs)
结构体用于创建自定义类型,字段可以包含多种数据类型。
实例
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
let user1 = User {
username: String::from("someusername"),
email: String::from("someone@example.com"),
sign_in_count: 1,
active: true,
};
枚举 (Enums)
枚举允许定义可能的几种数据类型中的一种。
实例
enum IpAddrKind {
V4,
V6,
}
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
模式匹配 (match)
match 是 Rust 中强大的控制流工具,类似于 switch 语句。
实例
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
fn main() {
another_function(5, 6);
}
fn another_function(x: i32, y: i32) {
println!("x 的值为 : {}", x);
println!("y 的值为 : {}", y);
}
fn main() {
fn five() -> i32 {
5
}
println!("five() 的值为: {}", five());
}
Rust 条件语句
fn main() {
let a = 12;
let b;
if a > 0 {
b = 1;
}
else if a < 0 {
b = -1;
}
else {
b = 0;
}
println!("b is {}", b);
}
Rust 循环
while 循环
while 循环是最典型的条件语句循环:
实例
fn main() {
let mut number = 1;
while number != 4 {
println!("{}", number);
number += 1;
}
println!("EXIT");
}
for 循环
for 循环是最常用的循环结构,常用来遍历一个线性数据结构(比如数组)。for 循环遍历数组:
实例
fn main() {
let a = [10, 20, 30, 40, 50];
for i in a.iter() {
println!("值为 : {}", i);
}
}
loop 循环
身经百战的开发者一定遇到过几次这样的情况:某个循环无法在开头和结尾判断是否继续进行循环,必须在循环体中间某处控制循环的进行。如果遇到这种情况,我们经常会在一个 while (true) 循环体里实现中途退出循环的操作。
Rust 语言有原生的无限循环结构 —— loop:
实例
fn main() {
let s = ['R', 'U', 'N', 'O', 'O', 'B'];
let mut i = 0;
loop {
let ch = s[i];
if ch == 'O' {
break;
}
println!("\'{}\'", ch);
i += 1;
}
}
克隆
Rust会尽可能地降低程序的运行成本,所以默认情况下,长度较大的数据存放在堆中,且采用移动的方式进行数据交互。但如果需要将数据单纯的复制一份以供他用,可以使用数据的第二种交互方式——克隆。
实例
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);
}
mod nation {
pub mod government {
pub fn govern() {}
}
mod congress {
pub fn legislate() {}
}
mod court {
fn judicial() {
super::congress::legislate();
}
}
}
fn main() {
nation::government::govern();
}
struct Person {
name: String,
age: i32,
}
// impl块:给Person增加方法
impl Person {
// 关联函数(类似C#静态构造/静态方法),惯例命名 new()
pub fn new(name: &str, age: i32) -> Self {
Self {
name: name.to_string(),
age,
}
}
// 实例方法:&self 不可变引用,只读,对应C#只读实例方法
pub fn say_hello(&self) {
println!("Hello, I'm {}, age {}", self.name, self.age);
}
// &mut self:可变引用,可以修改结构体内部字段
pub fn birthday(&mut self) {
self.age += 1;
}
// self(获取所有权):消耗这个实例,调用后原变量失效
pub fn consume(self) {
println!("{} will be dropped", self.name);
}
}
fn main() {
// 调用关联函数 new(),类似 new Person()
let mut p = Person::new("张三", 20);
p.say_hello();
p.birthday();
p.say_hello();
p.consume();
// p.say_hello(); // 编译报错,p所有权被move走了
}











Tauri 完整示例(包含常用交互)
版本说明:Tauri v2(当前稳定版),前端用纯 HTML/JS,不用框架,方便看懂底层交互;
包含功能:
前端调用 Rust 命令(同步 / 异步)
Rust 向前端发送事件 emit
文件读写(本地文件)
弹窗、消息对话框
获取系统信息
窗口控制(最小化、最大化、关闭)
剪贴板读写
本地存储(Tauri store)
打开外部链接
拖拽文件到窗口
## 1. 创建项目
```
# 先确保安装 tauri CLI
cargo install tauri-cli
# 创建项目
cargo tauri init
```
按需选择:前端名称 `frontend`,模板选 `Vanilla`(无框架)。
进入项目目录:
```
cd your-project-name
```
---
## 2. 修改 `src-tauri/Cargo.toml`
```
[package]
name = "tauri-demo"
version = "0.1.0"
edition = "2021"
[dependencies]
tauri = { version = "2.0", features = [
"shell-open",
"dialog",
"fs",
"clipboard-manager",
"window",
"store",
"event",
"path",
"os"
] }
serde = { version = "1.0", features = ["derive"] }
```
## 3. 修改 `src-tauri/src/main.rs`
```
use tauri::api::path::document_dir;
use tauri::ipc::InvokeError;
use tauri::{AppHandle, Manager, Runtime, State, Store};
use serde::Serialize;
use std::fs;
#[derive(Serialize)]
struct Msg {
content: String,
}
// 同步命令:简单加法
#[tauri::command]
fn add(a: i32, b: i32) -> i32 {
a + b
}
// 异步命令:延迟返回
#[tauri::command]
async fn delayed_greet(name: String) -> Result<String, InvokeError> {
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
Ok(format!("Hello, {}! 来自Rust异步命令", name))
}
// 读取文档目录下文件
#[tauri::command]
fn read_demo_file(filename: String) -> Result<String, InvokeError> {
let doc_dir = document_dir().ok_or_else(|| InvokeError::from("找不到文档目录"))?;
let path = doc_dir.join(filename);
let text = fs::read_to_string(path)?;
Ok(text)
}
// 写入文件
#[tauri::command]
fn write_demo_file(filename: String, content: String) -> Result<(), InvokeError> {
let doc_dir = document_dir().ok_or_else(|| InvokeError::from("找不到文档目录"))?;
let path = doc_dir.join(filename);
fs::write(path, content)?;
Ok(())
}
// Rust主动向前端推送事件
#[tauri::command]
fn send_event_to_frontend(app: AppHandle, msg: String) {
app.emit("rust-event", Msg { content: msg }).unwrap();
}
// 读取store
#[tauri::command]
fn get_store_val(store: State<Store>, key: String) -> Result<Option<String>, InvokeError> {
let v = store.get(&key);
Ok(v)
}
// 写入store
#[tauri::command]
fn set_store_val(store: State<Store>, key: String, value: String) -> Result<(), InvokeError> {
store.set(key, value);
store.save()?;
Ok(())
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.setup(|app| {
// 初始化持久化存储
let store = Store::new(app, "demo-store")?;
app.manage(store);
Ok(())
})
.invoke_handler(tauri::generate_handler![
add,
delayed_greet,
read_demo_file,
write_demo_file,
send_event_to_frontend,
get_store_val,
set_store_val
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
```
## 4. 修改 `src-tauri/tauri.conf.json`
> 重点:权限配置,不配置权限功能会被拦截!
```
{
"$schema": "https://schema.tauri.app/config/2.0",
"productName": "tauri-demo",
"version": "0.1.0",
"identifier": "com.tauri.demo",
"build": {
"frontendDist": "../frontend",
"devUrl": "http://localhost:1420"
},
"app": {
"windows": [
{
"title": "Tauri 全功能交互示例",
"width": 900,
"height": 650,
"resizable": true
}
],
"security": {
"csp": null
}
},
"bundle": {},
"plugins": {
"dialog": {},
"fs": {
"scope": ["$DOCUMENT/*"]
},
"shell": {
"scope": [
{
"name": "open-url",
"cmd": "open",
"args": {
"url": "https://github.com"
}
}
]
}
}
}
```
## 5. 前端 `frontend/index.html`
```
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Tauri 交互Demo</title>
<style>
*{box-sizing:border-box;font-family:system-ui}
body{padding:16px;max-width:900px;margin:0 auto}
button{padding:6px 10px;margin:4px;border:0;border-radius:4px;background:#2563eb;color:white;cursor:pointer}
textarea{width:100%;height:100px;padding:8px;margin:4px 0}
.log{background:#111;color:#0f0;padding:10px;border-radius:6px;height:180px;overflow:auto}
</style>
</head>
<body>
<h2>Tauri v2 常用交互演示</h2>
<div>
<h4>🔢 调用Rust同步命令 add(a,b)</h4>
<input id="a" value="10" type="number"> + <input id="b" value="20" type="number">
<button onclick="callAdd()">计算</button>
<span id="addRes"></span>
</div>
<div>
<h4>⏱️ 调用Rust异步命令</h4>
<input id="name" placeholder="输入名字">
<button onclick="callDelayed()">异步问候</button>
<div id="asyncRes"></div>
</div>
<div>
<h4>📢 Rust向前端发送事件(监听rust-event)</h4>
<input id="eventMsg" placeholder="发给前端的消息">
<button onclick="sendRustEvent()">发送事件</button>
</div>
<div>
<h4>💾 Store持久化存储</h4>
<input id="storeKey" placeholder="key" value="nick">
<input id="storeVal" placeholder="value">
<button onclick="setStore()">保存</button>
<button onclick="getStore()">读取</button>
<div id="storeOut"></div>
</div>
<div>
<h4>📁 文件读写(文档目录下)</h4>
<input id="fname" placeholder="文件名" value="test.txt">
<textarea id="fileContent">测试文本内容</textarea>
<button onclick="writeFile()">写入文件</button>
<button onclick="readFile()">读取文件</button>
</div>
<div>
<h4>💬 对话框弹窗</h4>
<button onclick="showMsg()">消息弹窗</button>
<button onclick="showConfirm()">确认对话框</button>
</div>
<div>
<h4>📋 剪贴板</h4>
<input id="clipText" value="剪贴板测试文字">
<button onclick="clipWrite()">写入剪贴板</button>
<button onclick="clipRead()">读取剪贴板</button>
<div id="clipOut"></div>
</div>
<div>
<h4>🪟 窗口控制</h4>
<button onclick="winMin()">最小化</button>
<button onclick="winMax()">最大化/还原</button>
<button onclick="winClose()">关闭窗口</button>
</div>
<div>
<h4>🔗 打开外部链接</h4>
<button onclick="openLink()">打开Github</button>
</div>
<div>
<h4>🗂️ 拖拽文件到窗口</h4>
<div id="dropArea" style="border:2px dashed #888;padding:20px">拖拽文件到这里</div>
<div id="dropResult"></div>
</div>
<div>
<h4>📜 日志输出</h4>
<div id="log"></div>
</div>
<script type="module">
import { invoke, event, shell, dialog, clipboard, window as tauriWin } from '@tauri-apps/api';
const logDom = document.getElementById('log');
function log(msg) {
logDom.innerText += msg + '\n';
logDom.scrollTop = logDom.scrollHeight;
}
// --- 1. 同步命令 add
async function callAdd() {
const a = Number(document.getElementById('a').value);
const b = Number(document.getElementById('b').value);
const res = await invoke('add', { a, b });
document.getElementById('addRes').innerText = `结果:${res}`;
log(`add(${a},${b}) = ${res}`);
}
// --- 2. 异步命令
async function callDelayed() {
const name = document.getElementById('name').value;
const res = await invoke('delayed_greet', { name });
document.getElementById('asyncRes').innerText = res;
log(`异步返回:${res}`);
}
// --- 3. Rust emit 事件监听
event.listen('rust-event', (e) => {
log(`收到Rust推送事件:${e.payload.content}`);
});
async function sendRustEvent() {
const m = document.getElementById('eventMsg').value;
await invoke('send_event_to_frontend', { msg: m });
}
// ---4. Store
async function setStore() {
const k = document.getElementById('storeKey').value;
const v = document.getElementById('storeVal').value;
await invoke('set_store_val', { key: k, value: v });
log(`store set ${k}=${v}`);
}
async function getStore() {
const k = document.getElementById('storeKey').value;
const v = await invoke('get_store_val', { key: k });
document.getElementById('storeOut').innerText = `值:${v}`;
log(`store get ${k} → ${v}`);
}
// ---5. 文件读写
async function writeFile() {
const fname = document.getElementById('fname').value;
const txt = document.getElementById('fileContent').value;
await invoke('write_demo_file', { filename: fname, content: txt });
log(`写入文件 ${fname} 成功`);
}
async function readFile() {
const fname = document.getElementById('fname').value;
const txt = await invoke('read_demo_file', { filename: fname });
document.getElementById('fileContent').value = txt;
log(`读取文件 ${fname}`);
}
// ---6. 弹窗
async function showMsg() {
await dialog.message('这是Tauri消息弹窗');
}
async function showConfirm() {
const ok = await dialog.confirm('确认吗?');
log(`确认框结果:${ok}`);
}
// ---7. 剪贴板
async function clipWrite() {
const t = document.getElementById('clipText').value;
await clipboard.writeText(t);
log('剪贴板写入成功');
}
async function clipRead() {
const t = await clipboard.readText();
document.getElementById('clipOut').innerText = t;
log(`剪贴板内容:${t}`);
}
// ---8. 窗口控制
const win = tauriWin.getCurrentWindow();
async function winMin() { await win.minimize(); }
async function winMax() { await win.toggleMaximize(); }
async function winClose() { await win.close(); }
// ---9. 打开外部链接
async function openLink() {
await shell.open('https://github.com');
}
// ---10. 拖拽文件
const dropArea = document.getElementById('dropArea');
dropArea.ondragover = e => e.preventDefault();
dropArea.ondrop = async (e) => {
e.preventDefault();
const files = e.dataTransfer.files;
const paths = [];
for (let f of files) paths.push(f.path);
document.getElementById('dropResult').innerText = paths.join('\n');
log(`拖拽文件:${paths.join(',')}`);
}
</script>
</body>
</html>
```
## 6. 运行
```
cargo tauri dev
```
---
# 功能清单核对
✅ 前端 ↔ Rust 双向通信(invoke + event)
✅ 持久化 Store
✅ 文件读写(受 scope 权限控制)
✅ 系统对话框 message /confirm
✅ 剪贴板读写
✅ 窗口最小化、最大化、关闭
✅ shell 打开外部网址
✅ 文件拖拽
✅ 同步命令、异步命令两种典型 IPC
# 重要提醒
1. Tauri 权限是**白名单机制**,fs、shell 都要在 `tauri.conf.json` 配置 scope,否则报错;
2. Tauri v1 和 v2 API 差异很大,上面是 v2;
3. `@tauri-apps/api` 需要前端安装:
```
cd frontend
npm install @tauri-apps/api
```
Rust 使用第三方库(crates),对比 Flutter pub
Rust 的包管理器叫 **Cargo**,包仓库是 https://crates.io,对应 Flutter 的 `pub.dev`;配置文件是 `Cargo.toml`,对应 Flutter 的 `pubspec.yaml`。
> 核心逻辑和 Flutter 很像:**声明依赖 → 下载依赖 → 代码导入使用**,但细节有区别。
## 1. 两种添加依赖方式
### 方式 1:手动写 Cargo.toml(类似你手动写 pubspec.yaml)
```
[package]
name = "demo"
version = "0.1.0"
edition = "2021"
[dependencies]
# 和 nfc_manager: ^3.2.0 对应
rand = "0.8.5"
# 也可以写版本兼容符号,^可以省略,rand = "0.8.5" 等价 rand = "^0.8.5"
```
写完之后**不会自动下载**,需要执行:
```
cargo build
# 或者只拉依赖不编译:cargo fetch
```
👉 和 `flutter pub get` 功能对应:`cargo fetch` 只下载包;`cargo build` 会**先检查依赖,缺失就自动下载,再编译**。
> ✅ 区别 Flutter:Flutter `flutter run` 不会自动 get;Rust `cargo build` / `cargo run` **会自动拉缺失的 crate**。
### 方式 2:命令直接添加(推荐,对应 `flutter pub add`)
```
cargo add rand@0.8.5
```
这条命令自动两件事:
1. 自动把 `rand = "0.8.5"` 写入 `Cargo.toml`
2. 自动下载更新依赖(等价自动 fetch)
## 2. 代码里面怎么导入使用
用 `use`,对应 Dart 的 `import`
```
use rand::Rng;
fn main() {
let num = rand::thread_rng().gen_range(1..=100);
println!("{}", num);
}
```
## 3. 缓存位置
- Flutter pub:全局缓存在 `~/.pub-cache`
- Rust crates:全局缓存在 `~/.cargo/registry`
源码都存在全局缓存,多个项目共用,不会每个项目复制一份源码。
## 4. 版本规则小说明(和 pub 类似但有细微差别)
```
rand = "0.8.5" # >=0.8.5, <0.9.0
rand = "0.8" # >=0.8.0, <0.9.0
rand = "=0.8.5" # 固定死只能 0.8.5,不升级
rand = ">0.8, <0.9" # 自定义区间
```
## 5. 几个高频常用命令对照(Flutter ↔ Rust)

## 6. 重点坑(和你刚才 nfc_manager 插件对比)
- Flutter 的插件:Dart 包 + Android/iOS 原生平台代码,需要单独配置清单、权限
- Rust 库分两类:
1. **纯 Rust crate**:直接 use 就能用,不用额外配置
2. **绑定原生的 crate(FFI)**,比如调用安卓 NFC、系统 SDK、C 库,会需要额外链接库、配置 `build.rs`、系统库,类似 flutter 平台插件的额外配置成本
> 举个例子:如果你想在 Rust 里搞 NFC,比如 `nfc` 相关 crate,它很可能是绑定底层 libnfc,你本地还要先装系统库,这点和 nfc_manager 插件要配置 Manifest/Info.plist 是一个道理。
## 7. 补充:离线 / 本地库怎么用
不想从 crates.io 下载,用本地路径,和 flutter path 依赖很像:
```
[dependencies]
my_lib = { path = "./my_lib" }
```
整体顺序:**先装 Rust 工具链 (rustup) → VSCode 插件 → 可选调试插件 → 新建项目**
### . 第一步:安装 rustup(核心,VSCode 插件只是编辑器辅助)
官网:https://www.rust-lang.org/tools/install
- Windows:先装 **Microsoft C++ 生成工具(Desktop 开发 C++)**,否则链接报错
然后执行安装脚本 `rustup-init.exe`,一路回车默认即可
- Mac / Linux:
```
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```
装好之后**新开终端**验证:
```
rustc --version
cargo --version
rustup --version
```
能输出版本号就成功。
> rustup 负责管理:rust 编译器 rustc、包管理器 cargo、不同版本的 Rust 工具链。
### 2. VSCode 必装插件(Ctrl+Shift+X 打开扩展面板)
1. **rust-analyzer(必装)**
提供:语法高亮、自动补全、跳转定义、hover 文档、错误提示、自动导入、代码重构、代码透镜(main 上方直接点 Run / Debug)Visual Stu...
2. **CodeLLDB(推荐,用来断点调试)**
Rust 在 VSCode 调试首选,Windows/Mac/Linux 通用;Windows 也可以用 C/C++ 插件配合 MSVC 调试,但 CodeLLDB 体验更好Microsoft ...
> 可选辅助插件:
> - crates:Cargo.toml 里直接提示版本、更新依赖(非常好用)
> - Even Better TOML:TOML 语法、格式化(Cargo.toml)
此调试插件在windows上不好用换windows上好用
调试终结方案
1. VSCode 扩展里,**卸载 CodeLLDB**
2. 安装微软官方的 `C/C++` 插件(ms-vscode.cpptools)
3. 在你的 rust 项目文件夹 `.vscode/launch.json`,粘贴下面完整配置:
```
{
"version": "0.2.0",
"configurations": [
{
"name": "Rust Debug (cppvsdbg)",
"type": "cppvsdbg",
"request": "launch",
"program": "${workspaceFolder}/target/debug/${workspaceFolderBasename}.exe",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"symbolSearchPath": "${workspaceFolder}/target/debug"
}
]
}
```
### 3. rust-analyzer 推荐设置(settings.json)
Ctrl+, 打开设置,右上角打开 `settings.json`,粘贴下面配置,适合大多数开发:
```
{
// 保存自动执行cargo check检查代码
"rust-analyzer.checkOnSave.command": "cargo",
"rust-analyzer.checkOnSave.extraArgs": ["check"],
// 开启类型内联提示、参数提示
"rust-analyzer.inlayHints.typeHints.enable": true,
"rust-analyzer.inlayHints.parameterHints.enable": true,
// 自动导入
"rust-analyzer.imports.granularity.group": "module",
// 保存自动格式化(用rustfmt)
"[rust]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "rust-lang.rust-analyzer"
}
}
```
rustfmt 格式化工具,rustup 自带,不用额外装。
### 4. 新建 & 打开 Rust 项目(和 Cargo 联动)
终端执行:
```
cargo new rust_demo
cd rust_demo
code .
```
打开之后 rust-analyzer 会自动识别 Cargo.toml,右下角会加载工程索引,等待一会儿。
打开 `src/main.rs`,`fn main()` 上方会出现 **Run | Debug** 透镜链接,直接点击运行 / 调试,不用手动写 launch.json(CodeLLDB 自动生成调试配置)rust-analy...。
### 5. 日常开发命令(VS 内置终端 Ctrl+`)
```
# 添加依赖(自动写入Cargo.toml + 拉取crate,对应flutter pub add)
cargo add rand
# 只下载依赖不编译
cargo fetch
# 编译+运行
cargo run
# 只检查错误,不生成二进制(速度很快,analyzer后台就是跑这个)
cargo check
# 发布构建(release,优化版)
cargo build --release
```
### 6. 对比你刚才 Flutter 的区别
- Flutter:`flutter pub get` 手动拉包;`flutter run` **不会自动下载依赖**
- Rust Cargo:`cargo run / build` **发现缺少依赖会自动执行下载**
- Flutter:插件是 Dart + 平台原生(Android/iOS)
- Rust crate:纯 Rust 库直接用;FFI 绑定 C / 系统库的 crate,需要本地提前装系统库、配置 build.rs
### 7. 常见踩坑
1. Windows 报错 `link.exe` 找不到:**就是没装 MSVC C++ 生成工具**,最常见问题
2. rust-analyzer 一直转圈、报 failed to load workspace:
- 终端先手动 `cargo check` 看有没有依赖报错、网络拉取 crates.io 超时
- 国内网络慢:配置 cargo 镜像(换清华 / 中科大 crates 镜像)
3. 不要同时装旧的 `rust-lang.rust(RLS)` 和 rust-analyzer,两者冲突
### 8. 镜像补充(国内必备,crates.io 访问慢)
在 `~/.cargo/config.toml`(Windows:`%USERPROFILE%\.cargo\config.toml`)
```
[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"
replace-with = 'ustc'
[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"
```
### 配套 rustup 镜像(可选,rustup update 加速)
如果你还要加速 rustup 更新(rustc 工具链):
Windows 系统环境变量新增两个变量:
```
RUSTUP_DIST_SERVER = https://rsproxy.cn/rustup
RUSTUP_UPDATE_ROOT = https://rsproxy.cn/rustup/rustup
```
vscode的配置文件在-》C:\Users\xn\AppData\Roaming\Code\User-》settings.json
{
"http.proxy": "http://127.0.0.1:7890",
"http.proxyStrictSSL": false,
"http.proxySupport": "override"
}
上一篇:如何用Python开发你的网站
下一篇:Python程序发布打包问题汇集