惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
Docker
G
Google Developers Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
GbyAI
GbyAI
V
Vulnerabilities – Threatpost
Hugging Face - Blog
Hugging Face - Blog
I
Intezer
S
Securelist
Forbes - Security
Forbes - Security
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Jina AI
Jina AI
Y
Y Combinator Blog
N
News | PayPal Newsroom
S
Schneier on Security
O
OpenAI News
T
The Blog of Author Tim Ferriss
V
Visual Studio Blog
Simon Willison's Weblog
Simon Willison's Weblog
Martin Fowler
Martin Fowler
人人都是产品经理
人人都是产品经理
雷峰网
雷峰网
NISL@THU
NISL@THU
阮一峰的网络日志
阮一峰的网络日志
WordPress大学
WordPress大学
N
News and Events Feed by Topic
Microsoft Azure Blog
Microsoft Azure Blog
P
Proofpoint News Feed
The Cloudflare Blog
Last Week in AI
Last Week in AI
博客园 - 司徒正美
L
LangChain Blog
C
CERT Recently Published Vulnerability Notes
L
LINUX DO - 热门话题
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
aimingoo的专栏
aimingoo的专栏
Apple Machine Learning Research
Apple Machine Learning Research
Recent Commits to openclaw:main
Recent Commits to openclaw:main
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
The Hacker News
The Hacker News
博客园 - Franky
Attack and Defense Labs
Attack and Defense Labs
Security Latest
Security Latest
T
Tailwind CSS Blog
博客园_首页
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Microsoft Security Blog
Microsoft Security Blog
V2EX - 技术
V2EX - 技术
腾讯CDC
V
V2EX

博客园 - PKICA

告别大显存依赖!用 Rust 新一代深度学习框架 Burn 打造纯 CPU 文本分类推理引擎 rust类型系统标记 编译配置解答 git实用命令 rust可变引用作为函数参数的机理详解 Rust内存重解释transmute C与Rust类型映射 Rust FFI 安全抽象范式 rust延迟初始化原语 rust重借用机制与原理 rust参数传递模型 汇编语言语法详解 gdb汇编调试 gdb-pwndbg的安装与使用指南 gdb调试插件gef C语言thread_local linux系统readelf命令使用指南 gcore转储进程内存 gdb查看命令 RGB与YUV颜色编码的区别 Rust原子类型 C++ STL求两个集合交集差集 gdb调试集锦 ubuntu24.0.4使用root用户登录 ubuntu24.0.4输入密码后跳回登录界面 AI内存压缩技术TurboQuant及存疑 ubuntu切换到指定内核版本 在没有顶级科技大佬直接背书的情况下deepseek为啥能够异军突起? HuggingFace和deepseek的关系 当前主流AI大模型 Rust写时克隆Cow系列2
rust底层设计理念值得注意的几个地方总结
PKICA · 2026-06-06 · via 博客园 - PKICA

学而不思则罔,思而不学则殆。孔老夫子常常教导我们学习方法,历经千年而常青。随口小提一下,不要无脑黑孔老夫子,他的部分治国思想有落后性,但整体要取其精华去其糟粕,学习思想也类似。不要把精力放在骂孔老夫子上,要学习他的优秀思想。

对于 C/C++ 开发者来说,Rust 的语法和底层逻辑既熟悉又陌生。对于主要从事c/c++开发12年之久的小兵来说,Rust 语言的底层设计理念(将内存管理、所有权和类型系统彻底暴露给程序员)导致了许多表面上看起来理所当然,但底层逻辑反直觉的语法。因为15年接触的项目使用到了rust,就用了不到一年,项目已结束,后面就不怎么接触rust了。直到几年前一个偶然的机会,再次重新点燃了rust学习和使用的火焰。

除了可变引用,以下是 Rust 中最常用、但也最容易让 C/C++ 开发者产生思维冲突和困惑的语法特性:

1. 生命周期参数(Lifetimes 'a

  • 类似概念:C++ 中的悬空指针/引用检查。
  • 困惑点:C++ 默认信任程序员能控住引用生命周期。Rust 要求在结构体或函数签名中显式用 'a 标注引用之间的绑定关系。
  • 核心机理:生命周期标注不会改变变量的实际存活时间,它只是通知编译器(借用检查器)多个引用之间的生存期约束,防止“容器还在,但里面的引用指向的对象已被销毁”的情况。

2. 模式匹配中的 refref mut

  • 类似概念:C++11 的结构化绑定(Structured Binding)与解引用。
  • 困惑点:在 matchif let 中,普通变量绑定会默认移动(Move)所有权。如果你只想借用部分字段,必须使用 ref
  • 核心机理:let ref x = y; 等价于 let x = &y;。在 C++ 中,你在左侧写 & 表示引用;而在 Rust 的模式匹配中,左侧写 & 表示解引用匹配,写 ref 才表示创建引用。
  • 赋值时:let p = &y;右侧的 &)表示创建引用,获取 y 的地址。
  • 模式匹配时:let &x = y;左侧的 &)才是解引用匹配,它会剥离掉 y 的引用,把绑定的值复制或移动给 x

matchif let 中匹配一个结构体或枚举时,如果你不小心,也会把里面的字段给 Move 掉。

struct User { name: String, age: u32 }
let user = User { name: String::from("Alice"), age: 30 };

match user {
    User { name, age } => { ... } // 💥 极其隐蔽的陷阱!
}
  • 为什么困惑:这里的 name 变量会user.name 的所有权 Move 出来!这导致匹配完成后,整个 user 变量直接报废(变成了部分初始化状态)。
  • 正确的做法:如果你只想看一眼,必须显式匹配为引用。在老版本 Rust 中需要写 User { ref name, age },现代 Rust 允许你在匹配目标前加引用:
    match &user { // 👈 对整个对象取引用
        User { name, age } => { ... } // 此时 name 自动推导为 &String
    }

3. 特征对象与动态分发(dyn Trait

  • 类似概念:C++ 的虚函数表(vtable)和基类指针 Base*
  • 困惑点:Rust 的 Trait(类似虚基类)不能直接作为变量类型或返回值,必须写成 &dyn TraitBox<dyn Trait>
  • 核心机理:C++ 的多态对象在胖指针内部包含 vptr。而 Rust 的 dyn Trait 是胖指针(Fat Pointer),包含两个部分:指向数据的指针指向虚函数表的指针。因此,dyn Trait 是一个不确定大小的类型(Unsized),必须隐藏在指针后面。

4. 移动语义(Move)与 Copy 特征

  • 类似概念:C++ 的移动构造函数(Move Constructor)与 std::move
  • 困惑点:Rust 中所有赋值和传参默认都是 Move,且 Move 之后原变量立刻失效(编译期拦截),这不需要写类似 std::move 的代码。
  • 核心机理:Rust 的 Move 仅仅是极为高效的内存浅拷贝(memcpy)。如果一个类型实现了 Copy trait(如 i32),赋值才会变成复制。C++ 移动后留下一个“有效但未定义”的空壳,而 Rust 移动后该内存直接不可访问。

5. 闭包的捕获方式(move ||

  • 类似概念:C++ 闭包的捕获列表 [&][=][values = std::move(x)]
  • 困惑点:Rust 闭包的捕获方式是由闭包体内的使用方式自动推导的,但有时必须显式加上 move 关键字
  • 核心机理:如果不加 move,闭包会默认尝试以最小权限(不可变引用 -> 可变引用 -> 移动)来捕获外部变量加上 move 关键字则强制将所有捕获变量的所有权转移进闭包这在多线程(如 thread::spawn)传输闭包时是强制要求的。
let mut text = String::from("Rust");

// 情况 A:不加 move
let print_text = || println!("{}", text); 
// 👆 此时闭包内部捕获的是 text 的【引用】(&String)

// 情况 B:加了 move
let print_text_move = move || println!("{}", text); 
// 👆 此时 text 的【所有权】被强行移动到了闭包内部

6. 智能指针的自动解引用强制多态(Deref Coercion)

  • 类似概念:C++ 的 operator->()operator*() 运算符重载。
  • 困惑点:为什么 String 类型的变量可以直接传给接收 &str 的函数?为什么 &Box<T> 可以直接当 &T 用?
  • 核心机理:Rust 编译器在发现类型不匹配时,如果该类型实现了 Deref trait,会自动插入解引用操作(例如把 &String 隐式转换为 &str)。这种“悄悄发生”的转换经常让习惯了 C++ 严格类型匹配的开发者感到困惑。

Rust 为了让智能指针(如 Box<T>, Rc<T>, Arc<T>, String)用起来像普通引用,引入了 Deref 特性。但这会导致你在调用方法时,根本分不清是谁在调用。

let s: String = String::from("hello");
// String 没有 .len() 方法,.len() 是定义在 str 切片上的。
// 但由于 Deref 存在,Rust 会自动把 &String 变成 &str
println!("{}", s.len()); 
  • 最让人抓狂的困惑:方法接收者(Receiver)的自动解引用与多重借用。当你调用一个方法时,Rust 编译器会自动尝试 &T&mut T*T(解引用一次)、**T(解引用两次)去匹配方法签名
    这导致有时候你写 &&&&&value.method()value.method() 的效果完全一样,初学者往往会彻底迷失在指针的层级里。

下班下班,回去休息。^-^

7. 数组/切片的 .iter().into_iter() 的隐式所有权转移

当你对一个集合进行循环时,写法的微小差异会导致完全不同的所有权行为。

let words = vec![String::from("hello"), String::from("world")];

// 困惑点 A:for-in 循环默认会“吃掉”集合
for w in words { ... } 
// 👆 这里的 words 发生了 Move!循环结束后,words 变量彻底死掉,无法再使用。

// 困惑点 B:.iter() 和 .into_iter() 截然不同
for w in words.iter() { ... }       // ✅ w 的类型是 &String(借用,words 还能用)
for w in words.into_iter() { ... }  // 💥 w 的类型是 String(Move,words 完蛋)
  • 为什么困惑:在其他语言中,for-in 只是单纯的遍历。但在 Rust 中,for-in 语法糖本质上是调用了 .into_iter()如果这个集合没有实现 Copy,它就会被无情地 Move 掉

8. String&str 的相加(+ 运算符的非对称性)

在字符串拼接时,Rust 的类型限制极其死板,且不支持对称操作。

let s1 = String::from("hello ");
let s2 = String::from("world");

let s3 = s1 + &s2; // ✅ 成功编译
// let s4 = &s1 + &s2; // ❌ 编译报错!
// let s5 = s1 + s2;   // ❌ 编译报错!
  • 为什么困惑:为什么左边必须是 String(不能带 &),而右边必须是 &str(或 &String)?
  • 底层逻辑:因为 Rust 中的 + 运算符本质上重载的是这个函数:
    fn add(self, rhs: &str) -> String // 👈 注意 self 是没有 & 的!
  • 这意味着 s1 + &s2 执行后,s1 的所有权已经被无情地消耗掉了(底层复用了 s1 的内存缓冲区以提高性能)。你不能用两个引用相加,因为那样就必须分配一块全新的内存,Rust 拒绝隐式做这种高开销的操作。

参考资料:

 rust工程化实践卷III juler 2015-11