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...
RustInPractice(03):String、切片与 UTF-8
Rust 不支持 text[0] 这种字符串下标,不是少了功能,而是拒绝把“第 0 个元素”伪装成一个没有歧义的操作。UTF-8 的一个字符可能占多个字节,字符、人眼看到的字形、字节索引也未必是一回事。 这是 RustInPractice 的第 03 篇。regex_demo 使用了字符串切片和原始字符串。下一篇进入数组、Vec、元组与迭代器。 一、String 和 &str 的边界 类型 含义 常见来源 String 拥有文本,可增长、可修改 文件、网络、拼接、String::from &str 借用的 UTF-8 文本切片 字符串字面量、String 的只读视图 12345let mut name = String::from("Rust");name.push_str(" language");let first_word: &str = &name[..4];println!("{first_word}"); 函数如果只需要读取文本,...
RustInPractice(02):Struct、枚举与模式匹配
很多程序把“状态”藏在整数、空指针或几个布尔字段中。Rust 更倾向于让状态直接出现在类型里:用 struct 聚合固定字段,用 enum 表达“若干种不同形状的可能性”,再用 match 强制处理所有分支。 这是 RustInPractice 的第 02 篇。对应 demo:enum_demo 与 oop_demo。下一篇解释 String、&str、切片和 UTF-8。 一、struct:数据与行为分开声明1234567891011121314struct Server { host: String, port: u16,}impl Server { fn new(host: String, port: u16) -> Self { Self { host, port } } fn address(&self) -> String { format!("{}:{...
RustInPractice(01):变量、类型、函数与控制流
Rust 初看有不少符号:let、mut、->、末尾分号。真正值得先建立的直觉只有一句:Rust 用不可变绑定做默认值,并把很多控制结构设计成会产生值的表达式。 这是 RustInPractice 的第 01 篇。上一篇完成 Cargo 环境;本篇的示例可以直接放进任意 main.rs 运行。下一篇进入 struct、enum 和模式匹配。 一、绑定默认不可变let 创建的是一个绑定。默认不能通过该绑定修改值: 12345let retries = 3;// retries += 1; // 编译错误let mut remaining = 3;remaining -= 1; 这不是说数据永远不能变,而是要求修改意图写在 mut 上。读到函数时,读者能先判断哪些局部状态会变化。 Rust 也允许 shadowing:同名新绑定遮住旧绑定。 12let input = "42";let input: u32 = input.parse().expect("literal is a valid integer"); 这里前一个 ...















