RustInPractice(11):Box、Drop 与递归数据结构
Box<T> 常被误解为“Rust 的手动堆内存”。实际上它仍然是普通拥有者:它拥有 T,离开作用域时自动释放 T。Box 的价值在于把值放到堆上,或让递归类型拥有一个编译期确定的大小。 这是 RustInPractice 的第 11 篇。对应 demo:box_demo、tree、stack_queue_list。下一篇讲共享所有权 Rc、Arc 与 Weak。 一、Box<T> 仍是唯一所有权12let number = Box::new(42);println!("{number}"); Box<T> 自身通常在栈上,内部指针指向堆上的 T。它不可隐式复制,移动 Box 就是移动唯一所有权。绝大多数场景不需要显式调用释放函数:作用域结束时自动执行 Drop。 二、Drop 统一管理各种资源demo 的 Resource 实现 Drop: 12345impl Drop for Resource { fn drop(&mut self) { pr...
RustInPractice(10):生命周期与引用关系
生命周期不是对象在运行时倒计时,也不是必须到处手写的神秘标记。它描述的是:一个引用至少要和它所指向的数据一样久。 大多数函数依靠省略规则自动推断;只有返回引用或在 struct 中保存引用时,才需要把关系写出来。 这是 RustInPractice 的第 10 篇。下一篇进入 Box<T> 和递归数据结构。 一、悬垂引用为何被禁止?1234// fn broken() -> &str {// let text = String::from("temporary");// &text// } text 在函数结束时被销毁,返回的引用会指向无效内存,所以编译器拒绝它。正确方案通常是返回拥有的数据: 123fn make_text() -> String { String::from("owned result")} 二、标注的是关系,不是具体时长从两个切片返回较长者: 123fn longer<'a>(left...
RustInPractice(09):借用与可变借用
按值传参会移动所有权,但多数函数只需要读取或修改调用者已有的数据。Rust 用引用表达这种临时访问,并用一条核心规则消除数据竞争:任意时刻,要么有多个只读引用,要么恰好有一个可变引用。 这是 RustInPractice 的第 09 篇。下一篇讲生命周期,解释编译器如何判断引用不会比它指向的数据活得更久。 一、不可变借用 &T1234567fn length(text: &str) -> usize { text.len()}let name = String::from("Rust");println!("{}", length(&name));println!("{name}"); length 得到的是只读视图,而不是 String 的所有权。函数结束后借用失效,原值仍由 name 拥有。对于文本参数,&str 比 &String 更通用:既能接收 String,也能接收字符串字面量。 二、可变...
RustInPractice(08):所有权、Move、Copy 与 Clone
Rust 的所有权不是“手动管理内存”的另一种说法,而是一套资源责任规则:一个值在任意时刻只有一个拥有者;拥有者离开作用域时,值会被自动清理;赋值和传参默认会转移所有权。 这是 RustInPractice 第二季的第 08 篇。对应 demo:ownership/move_demo。下一篇讲借用:不转移所有权,如何安全地访问一个值。 一、Move:责任转移,不是字节复制1234let first = String::from("hello");let second = first;// println!("{first}"); // 编译错误:first 已被移动println!("{second}"); String 管理一段堆内存。若赋值后两个变量都可用,它们可能在作用域结束时重复释放同一块资源。Rust 因而将赋值定义为 move:second 接管资源,first 失效。 这条规则也适用于函数: 123456fn consume(text: String) ...
YOLO 目标检测实战(00):系列导读
「跑通一个目标检测模型」听起来简单,但真正把它从环境、训练、评估一路带到能对外提供服务,中间有一堆坑:显卡驱动、CUDA 版本、共享内存、导出对齐、引擎版本绑定、服务预热……这个系列把这条链路完整走一遍,每一步都可复现、可回看。 这是「YOLO 目标检测实战」的第 00 篇(导读)。系列共 8 篇,以 Ultralytics YOLO 为例,串起 Docker GPU 环境 → 冒烟推理 → COCO128 短训 → 验证/预测 → ONNX 导出对齐 → TensorRT 基准 → FastAPI 部署 的完整闭环。文中路径统一用容器内 /workspace 或项目相对路径,硬件与版本号均以示例给出,不绑定具体机器或账号。 一、这个系列想解决什么网上「YOLO 五分钟上手」的教程很多,但它们通常停在「我在自己机器上 predict 出了一张带框的图」。真正做工程时,你会连续遇到这些问题: 环境不可复现:本机能跑,换台机器 / 换个同事就缺包、版本对不上。 训练一跑就崩:共享内存不足、OOM、DataLoader 卡死。 导出后「悄悄跑偏」:ONNX...
多模态微调实战(01):问题、目标与路线图
通用视觉语言模型「看得懂图、会说话」,但在垂直场景里往往不够稳。这个系列记录一次完整、可复现的多模态 LoRA 微调链路:从环境、数据一路走到全量训练、合并与 GGUF 量化发布。第一篇先不堆命令,而是把「要解决什么问题、为什么这么选、整条流水线长什么样」讲清楚。 这是「多模态微调实战」的第 01 篇。系列共 10 篇,以车牌 OCR 为例,串起 环境 → 数据 → 小规模验证 → 评测 → 全量训练 → 合并 → GGUF 量化 的完整闭环。框架用 ms-swift,基座用 Qwen3-VL-4B-Instruct,硬件前提是单卡约 16GB 显存的消费级 GPU。文中路径、主机名、账号均已泛化,车牌号均为虚构示例。 一、要解决什么问题以车牌 OCR 为例,通用 VLM 直接上会有几个典型问题: 基座模型可能擅自补全省份简称、加空格、换格式; 评测若要求「字符串完全一致」,Exact Match 会非常难看; 业务侧通常希望输出格式固定、字符集可控、延迟可接受。 因此更现实的路径是: 选一个够用的开源多模态基座(本系列用 Qwen3-VL-4B-Instruct)...
RustInPractice(07):格式化、Clippy、测试与文档
能编译不是完成。格式化解决风格噪声,Clippy 捕捉常见误用,测试验证行为,Rustdoc 把 API 的使用方式变成可执行说明。这四项组合起来,才是 Rust 项目的基础反馈环。 这是 RustInPractice 第一季的收官篇。对应 demo:format_demo、testing_demo。下一季从所有权、move、Copy 与 Clone 开始。 一、把检查命令放进日常循环1234cargo fmt --all -- --checkcargo clippy --workspace --all-targets -- -D warningscargo test --workspacecargo doc --no-deps fmt --check 只检查不改文件,适合 CI;本地修正格式直接运行 cargo fmt。-D warnings 让 Clippy 警告阻断流程,能防止“先忽略、以后再说”变成永久债务。规则不是绝对真理,必要时可局部说明原因后允许某条 lint,但不要无理由全局关闭。 二、Display 与 Debug 各司其职format_demo 为坐...
RustInPractice(06):Cargo 依赖、模块与工作区
一个 Rust 项目从单个 main.rs 长到多个命令、多个 crate 时,最容易混淆 package、crate、module 三个词。Cargo 的规则并不复杂:package 是构建单元,crate 是编译单元,module 是代码命名空间。 这是 RustInPractice 的第 06 篇。对应 demo:deps_demo、learning_guide。下一篇收拢格式化、Clippy、测试和文档。 一、Cargo.toml 管什么?最常见的依赖声明: 123[dependencies]clap = { version = "4", features = ["derive"] }serde = { version = "1", features = ["derive"] } 版本范围告诉 Cargo 可以选哪些兼容版本;Cargo.lock 记录一次解析后实际使用的精确版本。应用程序通常应提交 lockfile,以保证团队和 CI 使用相...
RustInPractice(05):Option、Result 与错误设计
Rust 没有传统意义的异常。可预期失败不靠隐式跳转,而是出现在函数签名中:Option<T> 表示可能没有值,Result<T, E> 表示成功值或明确错误。调用者不能假装没看见这个分支。 这是 RustInPractice 的第 05 篇。对应 demo:error_handling_demo。下一篇转向 Cargo 依赖、模块和 workspace。 一、先区分“不存在”和“失败”1234567fn find_user(id: u64) -> Option<String> { (id == 1).then(|| "Ada".to_string())}fn parse_port(text: &str) -> Result<u16, std::num::ParseIntError> { text.parse()} 找不到用户可能是正常查询结果,因此使用 Option。端口文本格式不合法则是可诊断的失败,使用 Result 并保留错...
RustInPractice(04):数组、Vec、元组与迭代器
集合是所有权规则最容易“看得见”的地方:同样是遍历,for item in values、for item in &values、for item in &mut values 的语义完全不同。本篇借 array_demo、tuple_demo 和 closure_demo 建立容器与迭代的基本选择。 这是 RustInPractice 的第 04 篇。对应 demo:array_demo、tuple_demo、closure_demo。下一篇用 Option、Result 和 ? 处理可恢复错误。 一、固定数组和动态 Vec[T; N] 的长度是类型的一部分,适合编译期已知数量的数据: 1let weekdays: [&str; 3] = ["Mon", "Tue", "Wed"]; Vec<T> 是可增长的连续容器,适合运行时才能知道长度的数据: 1234let mut scores = vec![80, 92, 76];scores.push(88);scores.re...















