Rust智能指针的奇妙之旅:从踩坑到顿悟

<数据集>咖啡叶片病害识别<目标检测> YOLO与VOC格式的咖啡叶片病害识别数据集,适用于YOLO系列、Faster Rcnn、SSD等模型训练,类别:Cercospora、Miner、Phoma、Rust,图片数量1744。文件中包含图片、txt标签、指定类别信息的yaml文件、xml标签,已将图片和txt标签划分为训练集、验证集和测试集,可直接用于YOLOv5、YOLOv6、YOLOv7、YOLOv8、YOLOv9、YOLOv10、YOLOv11、YOLOv12、YOLOv13、YOLOv26等YOLO系列算法的训练。 阅读详情

在这里插入图片描述

上个月我在写一个多线程文件处理工具时,程序莫名其妙地崩了。错误信息指向一个看似平常的变量传递,编译器冷冷地告诉我:“value moved here”。那一刻我才意识到,是时候认真搞懂Rust的智能指针了。Box、Rc、Arc,这三个家伙看起来都是在管理内存,但它们各自的脾气和适用场景完全不同。

Box:堆上数据的守门员

我第一次用Box是因为编译器逼的。当时写了个递归的二叉树结构:

// 这样写会报错
struct TreeNode {
    value: i32,
    left: TreeNode,   // 编译器:你这是要无限递归?
    right: TreeNode,
}

编译器直接拒绝编译,因为它无法确定TreeNode的大小。这时候Box就派上用场了:

struct TreeNode {
    value: i32,
    left: Option<Box<TreeNode>>,
    right: Option<Box<TreeNode>>,
}

fn main() {
    let leaf = TreeNode {
        value: 3,
        left: None,
        right: None,
    };
    
    let root = TreeNode {
        value: 1,
        left: Some(Box::new(leaf)),
        right: None,
    };
    
    println!("根节点值: {}", root.value);
}

Box把数据放到堆上,而栈上只存一个固定大小的指针。运行这段代码,输出"根节点值: 1",看起来平平无奇,但背后Box已经帮我们处理了内存分配和释放的所有细节。

Box的所有权规则很简单:谁拥有Box,谁就拥有堆上的数据。当Box离开作用域,堆上的数据会自动释放。这个特性让我在处理大对象时特别安心,不用担心内存泄漏。

温馨提示:Box虽然简单好用,但它遵循Rust严格的所有权规则。一个Box只能有一个所有者,如果你需要多个地方共享数据,Box就不够用了。

Rc:单线程里的共享管家

后来我写了个图结构来表示城市地铁线路,多条线路可能经过同一个站点。这时候Box就不行了,因为一个站点需要被多条线路"拥有"。Rc(Reference Counted)闪亮登场:

use std::rc::Rc;

struct Station {
    name: String,
    connections: Vec<String>,
}

fn main() {
    let central_station = Rc::new(Station {
        name: String::from("中央车站"),
        connections: vec![String::from("1号线"), String::from("2号线")],
    });
    
    let line1 = Rc::clone(&central_station);
    let line2 = Rc::clone(&central_station);
    
    println!("引用计数: {}", Rc::strong_count(&central_station));
    println!("1号线看到的站名: {}", line1.name);
    println!("2号线看到的站名: {}", line2.name);
}

运行结果显示引用计数为3,两条线路都能访问同一个车站数据。Rc在内部维护一个计数器,每次clone增加计数,每次drop减少计数,当计数归零时才真正释放内存。

我曾经天真地以为Rc::clone会复制整个数据,后来看了源码才知道,它只是增加计数器并返回一个新的指针,开销极小。这个认知错误让我之前写了很多性能糟糕的代码。

use std::rc::Rc;

fn process_data(data: Rc<Vec<i32>>) {
    // 直接使用,不需要解引用
    println!("数据长度: {}", data.len());
}

fn main() {
    let numbers = Rc::new(vec![1, 2, 3, 4, 5]);
    
    process_data(Rc::clone(&numbers));
    process_data(Rc::clone(&numbers));
    
    // numbers仍然有效
    println!("原始数据: {:?}", numbers);
}

这段代码完美运行,多次传递Rc几乎没有性能损耗。但Rc有个致命弱点:它不是线程安全的。

温馨提示:Rc只能在单线程中使用!如果你尝试在多线程间共享Rc,编译器会无情地拒绝你。这是我踩过最深的坑之一。

Arc:多线程世界的守护者

回到文章开头那个多线程文件处理工具的场景。我需要在多个线程间共享配置数据,Rc不行,那就该Arc(Atomic Reference Counted)出场了:

use std::sync::Arc;
use std::thread;

struct Config {
    thread_count: usize,
    buffer_size: usize,
}

fn main() {
    let config = Arc::new(Config {
        thread_count: 4,
        buffer_size: 1024,
    });
    
    let mut handles = vec![];
    
    for i in 0..3 {
        let config_clone = Arc::clone(&config);
        let handle = thread::spawn(move || {
            println!("线程 {} 读取配置: 缓冲区大小 = {}", 
                     i, config_clone.buffer_size);
        });
        handles.push(handle);
    }
    
    for handle in handles {
        handle.join().unwrap();
    }
    
    println!("主线程配置仍然有效: 线程数 = {}", config.thread_count);
}

运行这个程序,三个子线程都能正常访问配置数据,主线程也没有受影响。Arc使用原子操作来维护引用计数,保证了线程安全,但代价是比Rc稍慢一些。

在实际项目中,我遇到过这样的场景:需要在多个线程间共享并修改数据。Arc本身只提供不可变引用,这时候需要搭配Mutex或RwLock:

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];
    
    for _ in 0..5 {
        let counter_clone = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter_clone.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }
    
    for handle in handles {
        handle.join().unwrap();
    }
    
    println!("最终计数: {}", *counter.lock().unwrap());
}

这段代码输出"最终计数: 5",五个线程安全地递增了同一个计数器。Arc负责共享所有权,Mutex负责同步访问,两者配合天衣无缝。

温馨提示:Arc的原子操作比Rc慢,如果你的代码确定只在单线程中运行,用Rc就够了,不要为了"以防万一"就无脑用Arc,性能损失是实实在在的。

三种指针的抉择之道

经过几个月的实战,我总结出了一套选择规律:

Box适合单一所有权场景,特别是递归数据结构。如果你只是想把大对象放到堆上,或者需要trait对象,Box是首选。我在实现一个插件系统时,用Box存储不同类型的插件实例,效果完美。

Rc用于单线程内的共享所有权。图结构、缓存系统、观察者模式,这些场景下Rc大显身手。记住,Rc不能跨线程,这是铁律。

Arc是多线程共享的不二之选。线程池、全局配置、共享缓存,只要涉及多线程,就要考虑Arc。配合Mutex或RwLock,几乎可以解决所有并发共享问题。

有一次我在代码审查中看到同事写了这样的代码:

// 不推荐:过度包装
let data = Arc::new(Rc::new(Box::new(vec![1, 2, 3])));

这种层层嵌套毫无意义,反而增加了性能开销和心智负担。通常情况下,选一种智能指针就够了。

从迷茫到清晰

回顾这段学习历程,最大的收获不是记住了API,而是理解了Rust的设计哲学:明确所有权,安全第一。Box、Rc、Arc不是凭空设计的,它们分别对应了不同的所有权需求。

Box说:这是我的,谁也别想抢。Rc说:我们可以一起用,但只能在这个线程里。Arc说:大家都能用,跨线程也没问题,但要付出一点性能代价。

现在再遇到内存管理问题,我不会慌张了。先想清楚所有权关系:是独占还是共享?是单线程还是多线程?答案自然浮现。那个曾经让我头疼的文件处理工具,现在用Arc配合线程池,稳定运行了三个月,没出过任何内存问题。

Rust的学习曲线确实陡峭,但当你真正理解这些机制后,那种掌控感是其他语言给不了的。不是编译器在限制你,而是它在帮你避开无数潜在的坑。智能指针就是最好的证明。

75、CAN总线收发器共模范围扩展与抗干扰芯片选型 摘要:本文探讨了CAN总线收发器共模范围扩展与抗干扰芯片选型的关键问题。作者通过风电场的实际案例,指出共模电压超标会导致总线故障甚至芯片损坏。文章分析了不同芯片(如SN65HVD1050、TJA1043等)的共模范围特性,并提供了电路设计建议,包括正确使用共模扼流圈和终端电阻。最后强调选型时要结合实际工况,在共模范围、功耗、速度和成本之间取得平衡,普通工业现场建议选择正负36V共模范围的芯片。 阅读详情

相关推荐

车窗标定工作流程

标定简单来说就是修改防夹参数来适配对应的车型,所有的防夹的参数在vers_userdef.h文件中定义。#标定内容。

qq_21274991的博客 4271

程序员思维操作系统:用纯文本.log沉淀技术直觉与隐性经验

程序员日志不是技术博客,而是面向自我认知升级的底层实践工具。它基于时间戳与主题标签构建可检索、可回溯的知识坐标系,依托纯文本格式实现零格式负担,通过手写输入强制完成完整思考链,以‘不发布’原则保障认知安全与真实反思。其核心价值在于将调试顿悟、架构权衡、工具反思等隐性经验显性化、结构化、可验证化,形成对抗遗忘、校准直觉、驱动技术跃迁的个人认知基座。适用于从新人到技术负责人的全阶段开发者,在日常开发、故障复盘、学习沉淀与团队知识反哺中持续发挥工程实效。

天下无双 402

想在瑞芯微RK3588上跑视频分类模型?避开3D卷积这个坑,试试这几种NPU友好的方案

本文探讨了在瑞芯微RK3588上部署视频分类模型时如何避开3D卷积的性能陷阱,提供了三种NPU友好的替代方案:MobileNetV2+GRU组合、Temporal Shift Module(TSM)和帧级池化。这些方案在保持较高准确率的同时,充分利用RKNPU的硬件加速能力,显著提升推理效率。文章还分享了从模型转换到部署优化的完整流程和实战经验。

weixin_28724319的博客 418

GitHub 热榜项目:日榜(2026-11-06)

给编码智能体补工程与产品技能是本期最明显的一类,mattpocock/skills 把需求对齐、TDD、缺陷诊断做成小技能。NVIDIA/auto-ontology 从企业元数据生成本体、构建知识图谱,为 LLM 提供带来源追溯的自然语言查询和 SQL 生成,新增 2,679 星;vercel-labs/agent-desktop 通过桌面无障碍树把界面元素转成 JSON 和可操作 ref,让智能体不必截图就能点击输入,新增 2,173 星。

u130130的博客 454

从VBA到Python:探索CODE V API在MTF数据分析中的跨语言实践

本文详细介绍了如何从VBA转向Python,利用CODE V API实现MTF数据分析的高效工作流。通过Python的科学计算库和现代API设计,光学工程师可以大幅提升数据处理和可视化效率,实现自动化报告生成和性能优化。

weixin_29239661的博客 441

python3.12 在用django5.0创建超级用户时,遇到AttributeError: module ‘hashlib‘ has no attribute ‘pbkdf2_hmac‘的问题

产生问题的原因是python3.12的hashlib没有自带pbkdf2_hmac函数,该函数是加密算法的一种,只要通过别的函数导入pbkdf2_hmac即可。还有一种方法是将django默认使用的pbkdf2_hmac改为hashlib自带的sha2或其他算法返回加密的密码,笔者以后会尝试用此类办法解决问题。2.找到报错文件的位置/usr/local/lib/python3.12/site-packages/django/utils/crypto.py。保存修改的文件,重新执行创建超级用户的命令成功。

weixin_67057943的博客 852

GitHub 热榜项目:日榜(2026-11-07)

给编码智能体配技能的项目最热:cloudflare/security-audit-skill 把审计拆成六阶段并输出可验证 JSON,新增 2,548 星;mattpocock/skills 把工程技能放进 .agents 目录,新增 2,070 星。Web 应用框架也有四个项目集中上榜,以 react/react 为代表。

u130130的博客 383

OpenCV+ArUco实战:如何用Python实现高精度二维码测距(附完整代码)

本文详细介绍了如何使用Python和OpenCV实现基于ArUco标签的高精度单目视觉测距。通过相机标定、标签检测和三维位置估算,结合完整代码示例,帮助开发者在机器人导航、工业自动化等场景中实现厘米级距离测量,并探讨了精度优化与三维可视化等进阶应用。

weixin_29052717的博客 475

【Rust 手搓 Win11 灵动岛】① 从零搭骨架:无边框置顶窗口 + 工作区精确停靠

项目“岛仔”(daozai)是基于 Rust 与 eframe/egui 构建的 Win11 灵动岛桌面应用,采用窗口恒定展开、胶囊自绘方案实现无抖动丝滑动画。本篇聚焦 M1 里程碑:通过 Win32 获取工作区避让任务栏,解决创建期 DPI 缩放误传问题——虽首帧补偿逻辑因误解 ViewportCommand 单位而引入双重缩放错误,但实测假阳性掩盖了缺陷;后续将修复为直接下发 logical 尺寸。代码重构提升可测性,三组单元测试覆盖多 DPI 场景。最终验证结果经更正后确认需在下篇修正。

ygn12的博客 344

【Simulink教程案例18】基于simulink的基础OFDM+64QAM发射接收链路仿真

OFDM(Orthogonal Frequency Division Multiplexing)即正交频分复用技术,实际上OFDM是MCM(Multi Carrier Modulation),多载波调制的一种。通过频分复用实现高速串行数据的并行传输, 它具有较好的抗多径衰落的能力,能够支持多用户接入。OFDM技术由MCM(Multi-Carrier Modulation,多载波调制)发展而来。OFDM技术是多载波传输方案的实现方式之一,它的。

FPGA/MATLAB学习教程/源码/项目合作开发 1988

一篇文章认识《双目立体视觉》

双目立体视觉,由两个摄像头组成,像人的眼睛能看到三维的物体,具有深度的信息;单目视觉只能看到二维的物体。 1)双目摄像头 2)双目相机基线 3)打开双目摄像头 4)双目测距 视差disparity、 极线约束、 极线校正/立体校正、 a.双目标定 b.双目矫正 c.立体匹配 d.双目测距(三角测量) e.测距效果 立体视觉方法评测网站

黎国溥 7958

OpenSearch 的索引快照别只留本地:用 repository-s3 存进 RustFS

OpenSearch 跑起来,索引分片默认写在节点自己的本地盘上。单机测试很顺,可一旦索引量上来、节点不止一个,本地盘的故障就成了绕不开的坎:一块盘坏了,上面的分片副本丢了,重建全靠其他副本兜着,集群压力瞬间翻倍。更隐蔽的是快照,很多人把快照也留在本地盘的默认仓库里,等于把备份和原始数据放在了同一块会坏的盘上。OpenSearch 自带 repository-s3 插件,能把索引快照直接写进任意…

分布式存储与RustFS的博客 555

Linux ARM平台开发系列讲解(GPIO子系统) 2.3.2 GPIO子系统常用函数解析

1. 概述 该章节会记录一年常用的GPIO子系统常用函数,会更具情况不断更新 static inline int gpio_get_value(unsigned int gpio) 功能 :用于获取某个GPIO引脚上的值 gpio : gpio引脚号 返回值:非负值,得到的 GPIO 值;负值,获取失败。 static inline void gpio_set_value(unsigned int gpio, int value) static inline int gpio_cansleep(unsi

DSMGUOGUO的博客 668

Rolldown 把插件改动回传提速 26 倍

Rolldown 下一个版本会把插件改动的回传做细,只把真的改了的字段送回 Rust,大型项目里这一步从 1015 毫秒降到 39 毫秒。什么时候能用上呢,改动已经在 11 月 6 日合并,会跟着下一个版本走,Vite 8 用的就是 Rolldown。更麻烦的是缓存,Rust 替换 chunk 数据之后,之前那份代码缓存就废了,下一个读。这次没有动钩子对象本身,只是把「回传」这件事做细了,先看哪些字段变了,再决定传什么。所以改法很直接,只送变了的字段,没变的 chunk 一个字节都不发。

前端之虎陈随易 191

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

我写了一个 Windows 开发环境管理平台:Nexus(Tauri 2 + Rust + React)

文件监听事件很密集。如果节流窗口内的变更直接丢弃,最后一次改动就永远加载不到——所以窗口结束时必须补一轮。只对「失败」重试,不对「启动中」重试。启动慢不代表启动失败,盲目重试只会把健康的服务越搞越糟。Nexus 原本是为了解决自己日常开发里「窗口太多、状态不清、日志找不到」的问题,后来慢慢补成了现在这个样子:项目与服务管理、日志、服务健康、文件与代码查看、文件监听、开机启动、Node 版本管理,以及一个内嵌的 AI 助手。如果你也在 Windows 上同时维护多个服务,欢迎试用。

hi星尘的博客 563

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Airflow 的中间文件别散落各节点:让 DAG 直接读写 RustFS

数据流水线的中间结果往哪放,是个容易被忽略、出事才知道疼的问题。直接写本地盘,任务被调度到别的节点就找不到文件;挂共享 NFS,又多一层要维护的高可用。Airflow 自带的 Amazon S3 provider 其实就是个标准 S3 客户端,只要把连接的 endpoint_url 从 AWS 指到 RustFS,DAG 里的读写就全落到对象存储上了。这篇顺着官方集成指南,用 Apache Ai…

分布式存储与RustFS的博客 469

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

GitHub 热榜项目:周榜(2026-11-08)

本期收录19个热门开源项目,总新增⭐102,473,聚焦Python、TypeScript、Rust。Top项目涵盖AI智能体记忆(claude-mem)、Git工作树管理(worktrunk)、游戏开发协作框架(Claude-Code-Game-Studios)及紫微斗数排盘引擎(ziwei-doushu)。T3code实现多智能体控制界面,Anthropic开源技能库助力Agent开发,Hallmark则专注消除AI设计“机器味”。技术趋势凸显AI工程化与跨会话智能体能力的快速演进。

u130130的博客 702

Rust 如何构建高可靠支付回调系统?从《Rust 系统编

在现代支付系统中,支付回调处理和对账逻辑是整个系统的稳定性关键。当一笔支付完成时,第三方支付平台会通过异步回调通知业务系统交易结果。然而,在高并发、高吞吐的场景下,如何保证这些回调消息的可靠消费与数据一致性,成为了一个挑战。作为一位中级开发者,你可能已经经历过类似场景:在处理支付回调时,因网络抖动、服务宕机、重复通知等问题导致的数据不一致或对账失败。这时候,“系统编程”不再是理论层面的概念,而是实际落地时的救命稻草。

wuchangjian的专栏 210

Worktrunk:用 Rust 写的 CLI,给并行跑的 AI agent 各开一份 Git worktree

Worktrunk 是一个 Rust 写的 Git worktree 管理 CLI,补上了原生 worktree 缺失的创建、命名、切换、清理生命周期,目标是让并行跑的多个 AI agent 各占一份互不干扰的工作副本;截至 2026 年 11 月 3 日仓库 10313 stars、81 位贡献者,但本文仅有单一来源,安装方式与 CLI 命令未获证实。

老孙正经胡说 165

Rust重写开源版Photoshop来了,支持PSD编辑,还能让AI Agent直接操作图像

PhotoCraft 是一个基于 Rust 开发的开源专业图像编辑器,旨在以原生高性能架构重构 Photoshop 级工作流。它提供图层、蒙版、调整图层、矢量绘图、文字排版、智能滤镜以及高兼容性的 PSD 支持,同时通过 CLI、JSON 控制接口和 MCP Server 具备面向 AI Agent 的自动化能力。

Tony.Dong的专栏 246

用Rust重写IntelliJ IDEA:169MB替代2GB,0.9秒打开项目的开源IDE

IntelliJ IDEA虽强大却臃肿,启动慢、占内存。开源项目OpenIDEA用Rust重写,保留熟悉快捷键与工作流,体积仅169MB,0.9秒启动。基于LSP支持60+语言,结合Tauri实现轻量跨平台界面。虽暂不替代IntelliJ的Java深度与插件生态,但为多语言开发者提供高效轻量选择,展现Rust+Web技术构建桌面应用的新范式。

weixin_41007138的博客 186

target 目录越来越大,Rust 项目该怎么清理

Rust 项目一多,磁盘空间常常先被target目录吃掉。每个工作区的target里,既有最后要运行的程序,也有依赖编译结果、增量编译数据和构建脚本产生的中间文件。它们都在增长,但生命周期并不一样。整目录删除当然最快,代价是最终产物也会一起消失。只想清掉缓存,又得挨个项目判断哪些目录能删。这个两难,是我写的原因。cargoc 把 Cargo 的中间构建文件放到专用目录,按工作区记录和清理空闲缓存;最终产物仍留在 Cargo 原来的target-dir。它解决的是“哪些中间文件可以清、什么时候清”。

fuwei2020的博客 343

我用 RustyPaste 做 AI Agent 云端存储

RustyPaste 是一个用 Rust 编写的极简文件上传与 Pastebin 服务。可以用来存储和访问 AI Agent 生成的文件

藏云阁技术社区 200

云服务器 + 公网 IP + 容器化部署 (Docker /RustDesk / Rtmp)

我偶然找到的腾讯云 轻量应用服务器,可以免费试用1个月,体验一下 PC远程控制和推拉流。服务器系统选择的 Ubuntu 22.04.5 LTS1、在控制台重置密码2、给服务器 添加放行规则在防火墙页面,点击222111619353、用 SSH 工具(比如 MobaXterm 或 Xshell)通过username@服务器公网IP连上服务器。

2301_79809746的博客 206

从 Electron 到 GPUI,9 个跨平台桌面方案该怎么选

把 Electron、Tauri、Wails、MyGo、Flutter、GPUI、Rune、Native Shell、PyQt 放在一起,按技术栈、渲染方式、成熟度和热度逐个对比,最后给出一张按团队背景选型的速查表。

313

每日极客日报 · 2026年11月08日

今日精选 30 条 IT 科技热点,覆盖 AI 大模型、开源生态、云原生、工程实践与业界动态。

weixin_45635831的博客 167
上一篇: 丹摩征文活动 | ChatGLM-6B超大规模AI对话:AI工程师的实践与探索
下一篇: 从Vec遍历的性能陷阱,聊聊Rust的零拷贝迭代器模式
剑指~巅峰
博客等级 码龄3年 323粉丝 18原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值