“Rust思维下的C++编程”:在C++中,如何应用Rust中的概念?

C语言与CPP编程 2024-09-02 09:00

本文经授权转自公众号CSDN(ID:CSDNnews)

整理 | 郑丽媛

【编者按】自从美国白宫对开发者呼吁,“停止使用 C 和 C++,改用 Rust 等内存安全编程语言”后,两方之间从未停止的争论就被推到了一个新高度。而在这之中,也有部分 C++ 开发者提议:或许 Rust 中的一些概念,可以试着运用到 C++ 编程中?


近日,一位开发者(ID:delta242)在 Reddit 上发了一篇长文《在 C++ 中应用 Rust 的概念》,里面提到了一些可用于改善 C++ 代码的 Rust 概念,引来了诸多关注和讨论。

根据他在开篇的介绍,“虽然我不是 Rust 专家,但我很喜欢这门语言的许多概念。在日常编程中,我主要用 C++,而现在我经常会运用一些 Rust 的概念来改善我的 C++ 代码”,可以看出,下面是他亲身实践过的、可用于优化 C++ 代码的 Rust 概念。


1、在 C++ 中,如何应用 Rust 的概念?

(以下为他的长文翻译:)

(1)带值的枚举

我很喜欢 Rust 的枚举,因为你可以给枚举常量赋值(例如,Option 枚举中有一个没有值的 None 和一个有值的 Some)。在类型理论中,这通常被称为代数数据类型,而在 C++ 中,我们有 variants,可以定义辅助结构体来实现类似的功能:

struct Some { T value; };struct None { };using Optional = std::variant;

(注:这个例子可能有点蠢,因为 std::optional 要好得多。但对于更复杂的类型来说,这具有一定参考意义。)

(2)CRTP 和 Traits

在 Rust 中,Traits 用于定义类型的共享功能。而在 C++ 中,我们可以用 CRTP 在编译时强制类实现特定的函数来实现静态多态性。CRTP 还允许在基类中实现默认功能,我以前曾用这种方法来定义迭代器类型,只要基类实现了 operator[],就可以减少大量模板代码的编写。

(3)字符串格式化

在 C++ 中,如果向 std::format 传递的参数数量多于格式字符串中的占位符,并不会导致编译时错误。我曾经遇到过这样的 bug,例如由于缺少占位符,日志消息中缺少了某些信息,导致与代码中不一致。

而这个情况如果放在 Rust 中,就会产生编译时错误。所以这对于 C++ 来说,将是一个简单而实用的改进,有助于提高代码质量和开发效率。

(4)拥有 Mutex

在 Rust 中,Mutex 类型拥有受保护的值。我非常喜欢这个概念,这样不获取 Mutex 就无法访问受保护的值(这在 C++ 中经常发生)。有一个简单的技巧来实现类似效果,那就是在 C++ 中写一个具有 lock 函数的封装 Mutex 类,该函数将接受一个带有对受保护值的引用的 lambda 表达式作为参数。由于 Rust 中有借用检查器,这样的操作总是安全的,而在 C++ 中,误用很容易再次导致竞争条件,但至少通过这样的封装器,这种情况就不那么容易发生了。

(5)内部可变性

Rust 在安全的情况下会使用内部可变性(即使变量是 const),例如当一个值受 Mutex 保护时。在 C++ 中,我们也可以采用类似的想法,例如“const 表示线程安全”。

(6)IIFE

在 Rust 中,每个作用域都是一个表达式,这样可以很好地将变量限制在更小的作用域中。而在 C++ 中,我们可以用 lamdas 表达式来使用立即调用的函数表达式(IIFE)来达到同样的效果:

auto value = [] {  // Complex initializer  return result;}(); // notice the invocation

以上,就是我现在能想到的。


2、“Rust 让我成为了一名更好的 C++ 开发者”

在这篇长文下,不少开发者也分享了自己在 C++ 编程中借鉴 Rust 概念的心得,甚至直言“Rust 让我成为了一名更好的 C++ 开发者”。

(1)“最近,我养成了在 C++ 中使用“match”宏的这个习惯,我很喜欢。”

template struct overloaded : Ts... { using Ts::operator()...; };
template auto match(Val val, Ts... ts) { return std::visit(overloaded{ts...}, val);}

(2)“重载非常好,我觉得它可以成为 STL 的一部分。此外,有了 C++20 模板化的 lambdas,还可以编写一些非常花哨的代码。”

visit(  overloaded {    [] T>(T value) {}    [](auto other) {}  }, value)

对此,一位开发者感慨:“这正是我希望看到的,虽然我不喜欢 Rust,但它确实有一些 C++ 可以借鉴的做法,更安全总归是好的。”


3、在 C++ 中应用 Rust 概念的一些失败案例

不过与此同时,也有开发者提醒“必须小心”:以 Rust 的 Mutex 为例,当你访问 Mutex 中的数据时,不可能将该指针存储下来,然后在解锁 Mutex 后再访问数据(忽略特殊情况)。你可以在 C++ 中实现一个拥有 Mutex 的类,但编译器不会在意你是否在锁的作用域之外持有一个指向受保护数据的指针,并在未受保护的情况下访问它。

针对这个话题,开源搜索引擎 Meilisearch 的高级工程师 Louis Dureuil 曾写过一篇相关文章《这对 C++ 来说太危险了》:“一些设计模式之所以实用,归功于 Rust 的内存安全性,而在 C++ 中使用则过于危险。”

在文中,Louis Dureuil 分享了他在 C++ 中应用 Rust 概念的失败案例。

当时,他正在用 Rust 编写一个内部库,其中有一个他希望能克隆、而不会复制其中数据的错误类型。在 Rust 中,这需要使用引用计数指针,比如 Rc。他编写了一个错误类型,将其用作可能发生错误的函数的错误变体,继续了他的工作。

struct Error {    data: Rc,}
pub type Response = Result;
fn parse(input: Input) -> Response { todo!()}

后来他发现,对某些输入进行解析需要很长时间,于是决定通过通道将输入发送到另一个线程,并通过另一个通道获取响应,这样长时间的解析就不会阻塞主线程。

enum Command {    Input(Input),    Exit,}
pub enum RequestStatus { Completed(Response), Running,}
pub struct Parser { command_sender: Sender, response_receiver: Receiver<(Input, Response)>, cached_result: HashMap,}
impl Parser { pub fn new() -> Self { let (command_sender, command_receiver) = channel::(); let (response_sender, response_receiver) = channel::<(Input, Response)>();
std::thread::spawn(move || loop { match command_receiver.recv() { Ok(Command::Input(input)) => { let response = parse(input); let _ = response_sender.send((input, response)); } Ok(Command::Exit) => break, Err(_) => break, } });
Self { command_sender, response_receiver, cached_result: HashMap::default(), } }
pub fn request_parsing(&mut self, input: Input) -> RequestStatus { // pump previously received responses while let Ok((input, response)) = self.response.receiver.try_recv() { self.cached_result .insert(input, RequestStatus::Completed(response)); }
let response = match self.cached_result.entry(input) { Entry::Vacant(entry) => { self.command_sender .send(Command::Input(entry.key())) .unwrap(); entry.insert(RequestStatus::Running) } Entry::Occupied(entry) => entry.into_mut(), }; response.clone() }}

然而,在进行这一更改时,Louis Dureuil 收到了以下错误信息:

error[E0277]: `Rc` cannot be sent between threads safely   --> src/main.rs:58:32    |58  |               std::thread::spawn(move || loop {    |  _____________------------------_^    | |             |    | |             required by a bound introduced by this call59  | |                 match command_receiver.recv() {60  | |                     Ok(Command::Input(input)) => {61  | |                         let response = maybe_make(input);...   |68  | |                 }69  | |             });    | |_____________^ `Rc` cannot be sent between threads safely    |    = help: within `(&'static str, Result)`, the trait `Send` is not implemented for `Rc`note: required because it appears within the type `Error`   --> src/main.rs:17:16    |17  |     pub struct Error {    |                ^^^^^note: required because it appears within the type `Result`   --> /home/dureuill/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/result.rs:502:10    |502 | pub enum Result {    |          ^^^^^^    = note: required because it appears within the type `(&str, Result)`    = note: required for `Sender<(&'static str, Result)>` to implement `Send`note: required because it's used within this closure   --> src/main.rs:58:32    |58  |             std::thread::spawn(move || loop {    |                                ^^^^^^^note: required by a bound in `spawn`   --> /home/dureuill/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/mod.rs:683:8    |680 | pub fn spawn(f: F) -> JoinHandle    |        ----- required by a bound in this function...683 |     F: Send + 'static,    |        ^^^^ required by this bound in `spawn`

正如编译器所解释的那样,这是因为 Rc 类型不支持在线程之间发送,因为这样会导致数据竞争。实际上,Rc 中的引用计数并不以原子方式进行操作,而是使用常规的整数操作。

为了实现线程安全的引用计数,Rust 提供了另一种类型 Arc,它使用原子引用计数,而将代码修改为使用 Arc 非常简单:

diff --git a/src/main.rs b/src/main.rsindex 04ec0d0..fd4b447 100644--- a/src/main.rs+++ b/src/main.rs@@ -3,9 +3,9 @@ use std::{io::Write, time::Duration}; mod parse {     use std::{         collections::{hash_map::Entry, HashMap},-        rc::Rc,         sync::{             mpsc::{channel, Receiver, Sender},+            Arc,         },         time::Duration,     };@@ -15,13 +15,13 @@ mod parse {
#[derive(Clone, Debug)] pub struct Error {- data: Rc,+ data: Arc, }
impl Error { fn new(data: ExpensiveToCloneDirectly) -> Self { Self {- data: Rc::new(data),+ data: Arc::new(data), } } }

也就是说,只要不需要引用原子操作的计数,就可以使用 Rc。但当需要线程安全时,编译器会强制 Louis Dureuil 切换到 Arc,并带来了原子引用计数的开销。

Louis Dureuil 指出,这个原则也深受 C++ 开发者的喜爱。但与 Rust 完全不同的是,在 C++ 中,标准库中只有带有原子引用计数的 shared_ptr,它相当于 Arc,而不是 Rc——所以,即使你不使用原子操作,也仍要为原子引用计数付出代价。

最后,一句话总结:在 C++ 中适当应用 Rust 概念固然不错,但切记不要根据在 Rust 中会发生的情况,对 C++ 也做出相同的假设。

参考链接:

https://www.reddit.com/r/cpp/comments/1bx7wjm/applying_concepts_from_rust_in_c/

https://blog.dureuill.net/articles/too-dangerous-cpp/

本文转自公众号“CSDN”,ID:CSDNnews

分享个群友推荐的招聘类小程序

这里分享一个交流群群友推荐的招聘小程序,其中有很多二三线城市,比如赤峰、保定、阿克苏之类的三四线城市,还支持按照岗位、地点和薪资要求来找合适的岗位。

经常遇到一些想回老家或者二三线城市的同学苦于没有合适的去处,打开boss直聘和猎聘网这些招聘类软件,结果发现好的岗位基本都集中在一线城市,很少看到那种有二三线城市的招聘岗位。

比如说我是一个河南人,以后想回老家省会郑州求职的话,我可以筛选岗位为Linux C/C++开发、地点为郑州、薪资为12-18k的工作,那么直接筛选即可。

昨天已经在几个交流群中分享了一下,今天也给大家分享一二,扫描下方二维码注册个账号即可体验。

这几年行情不好,有好的工作机会还是一起分享,报团取暖才是王道。如果有公司负责人之类的朋友想要招聘,可以直接私聊我,必须帮忙扩散分享~

EOF

日常分享C/C++、计算机学习经验、工作体会,欢迎点击此处查看我以前的学习笔记&经验&分享的资源。

我组建了一些社群一起交流,群里有大牛也有小白,如果你有意可以一起进群交流。

欢迎你添加我的微信,我拉你进技术交流群。此外,我也会经常在微信上分享一些计算机学习经验以及工作体验,还有一些内推机会

加个微信,打开另一扇窗

经常遇到有读者后台私信想要一些编程学习资源,这里分享 1T 的编程电子书、C/C++开发手册、Github上182K+的架构路线图、LeetCode算法刷题笔记等精品学习资料,点击下方公众号会回复"编程"即可免费领取~

感谢你的分享,点赞,在看三  

C语言与CPP编程 C语言/C++开发,C语言/C++基础知识,C语言/C++学习路线,C语言/C++进阶,数据结构;算法;python;计算机基础等
评论
  • 一、VSM的基本原理震动样品磁强计(Vibrating Sample Magnetometer,简称VSM)是一种灵敏且高效的磁性测量仪器。其基本工作原理是利用震动样品在探测线圈中引起的变化磁场来产生感应电压,这个感应电压与样品的磁矩成正比。因此,通过测量这个感应电压,我们就能够精确地确定样品的磁矩。在VSM中,被测量的样品通常被固定在一个震动头上,并以一定的频率和振幅震动。这种震动在探测线圈中引起了变化的磁通量,从而产生了一个交流电信号。这个信号的幅度和样品的磁矩有着直接的关系。因此,通过仔细
    锦正茂科技 2025-02-28 13:30 100浏览
  • 在物联网领域中,无线射频技术作为设备间通信的核心手段,已深度渗透工业自动化、智慧城市及智能家居等多元场景。然而,随着物联网设备接入规模的不断扩大,如何降低运维成本,提升通信数据的传输速度和响应时间,实现更广泛、更稳定的覆盖已成为当前亟待解决的系统性难题。SoC无线收发模块-RFM25A12在此背景下,华普微创新推出了一款高性能、远距离与高性价比的Sub-GHz无线SoC收发模块RFM25A12,旨在提升射频性能以满足行业中日益增长与复杂的设备互联需求。值得一提的是,RFM25A12还支持Wi-S
    华普微HOPERF 2025-02-28 09:06 143浏览
  • 请移步 gitee 仓库 https://gitee.com/Newcapec_cn/LiteOS-M_V5.0.2-Release_STM32F103_CubeMX/blob/main/Docs/%E5%9F%BA%E4%BA%8ESTM32F103RCT6%E7%A7%BB%E6%A4%8DLiteOS-M-V5.0.2-Release.md基于STM32F103RCT6移植LiteOS-M-V5.0.2-Release下载源码kernel_liteos_m: OpenHarmon
    逮到一只程序猿 2025-02-27 08:56 195浏览
  • Matter 协议,原名 CHIP(Connected Home over IP),是由苹果、谷歌、亚马逊和三星等科技巨头联合ZigBee联盟(现连接标准联盟CSA)共同推出的一套基于IP协议的智能家居连接标准,旨在打破智能家居设备之间的 “语言障碍”,实现真正的互联互通。然而,目标与现实之间总有落差,前期阶段的Matter 协议由于设备支持类型有限、设备生态协同滞后以及设备通信协议割裂等原因,并未能彻底消除智能家居中的“设备孤岛”现象,但随着2025年的到来,这些现象都将得到完美的解决。近期,
    华普微HOPERF 2025-02-27 10:32 212浏览
  •           近日受某专业机构邀请,参加了官方举办的《广东省科技创新条例》宣讲会。在与会之前,作为一名技术工作者一直认为技术的法例都是保密和侵权方面的,而潜意识中感觉法律有束缚创新工作的进行可能。通过一个上午学习新法,对广东省的科技创新有了新的认识。广东是改革的前沿阵地,是科技创新的沃土,企业是创新的主要个体。《广东省科技创新条例》是广东省为促进科技创新、推动高质量发展而制定的地方性法规,主要内容包括: 总则:明确立法目
    广州铁金刚 2025-02-28 10:14 103浏览
  •         近日,广电计量在聚焦离子束(FIB)领域编写的专业著作《聚焦离子束:失效分析》正式出版,填补了国内聚焦离子束领域实践性专业书籍的空白,为该领域的技术发展与知识传播提供了重要助力。         随着芯片技术不断发展,芯片的集成度越来越高,结构也日益复杂。这使得传统的失效分析方法面临巨大挑战。FIB技术的出现,为芯片失效分析带来了新的解决方案。它能够在纳米尺度上对芯片进行精确加工和分析。当芯
    广电计量 2025-02-28 09:15 116浏览
  • 1,微软下载免费Visual Studio Code2,安装C/C++插件,如果无法直接点击下载, 可以选择手动install from VSIX:ms-vscode.cpptools-1.23.6@win32-x64.vsix3,安装C/C++编译器MniGW (MinGW在 Windows 环境下提供类似于 Unix/Linux 环境下的开发工具,使开发者能够轻松地在 Windows 上编写和编译 C、C++ 等程序.)4,C/C++插件扩展设置中添加Include Path 5,
    黎查 2025-02-28 14:39 140浏览
  • 2025年2月26日,广州】全球领先的AIoT服务商机智云正式发布“Gokit5 AI智能体开发板”,该产品作为行业首个全栈式AIoT开发中枢,深度融合火山引擎云原生架构、豆包多模态大模型、扣子智能体平台和机智云Aiot开发平台,首次实现智能体开发全流程工业化生产模式。通过「扣子+机智云」双引擎协同架构与API开放生态,开发者仅需半天即可完成智能体开发、测试、发布到硬件应用的全流程,标志着智能体开发进入分钟级响应时代。一、开发框架零代码部署,构建高效开发生态Gokit5 AI智能体开发板采用 “
    机智云物联网 2025-02-26 19:01 162浏览
  • 振动样品磁强计是一种用于测量材料磁性的精密仪器,广泛应用于科研、工业检测等领域。然而,其测量准确度会受到多种因素的影响,下面我们将逐一分析这些因素。一、温度因素温度是影响振动样品磁强计测量准确度的重要因素之一。随着温度的变化,材料的磁性也会发生变化,从而影响测量结果的准确性。因此,在进行磁性测量时,应确保恒温环境,以减少温度波动对测量结果的影响。二、样品制备样品的制备过程同样会影响振动样品磁强计的测量准确度。样品的形状、尺寸和表面处理等因素都会对测量结果产生影响。为了确保测量准确度,应严格按照规
    锦正茂科技 2025-02-28 14:05 134浏览
  • RGB灯光无法同步?细致的动态光效设定反而成为产品客诉来源!随着科技的进步和消费者需求变化,电脑接口设备单一功能性已无法满足市场需求,因此在产品上增加「动态光效」的形式便应运而生,藉此吸引消费者目光。这种RGB灯光效果,不仅能增强电脑周边产品的视觉吸引力,还能为用户提供个性化的体验,展现独特自我风格。如今,笔记本电脑、键盘、鼠标、鼠标垫、耳机、显示器等多种电脑接口设备多数已配备动态光效。这些设备的灯光效果会随着音乐节奏、游戏情节或使用者的设置而变化。想象一个画面,当一名游戏玩家,按下电源开关,整
    百佳泰测试实验室 2025-02-27 14:15 137浏览
  • 更多生命体征指标风靡的背后都只有一个原因:更多人将健康排在人生第一顺位!“AGEs,也就是晚期糖基化终末产物,英文名Advanced Glycation End-products,是存在于我们体内的一种代谢产物” 艾迈斯欧司朗亚太区健康监测高级市场经理王亚琴说道,“相信业内的朋友都会有关注,最近该指标的热度很高,它可以用来评估人的生活方式是否健康。”据悉,AGEs是可穿戴健康监测领域的一个“萌新”指标,近来备受关注。如果站在学术角度来理解它,那么AGEs是在非酶促条件下,蛋白质、氨基酸
    艾迈斯欧司朗 2025-02-27 14:50 400浏览
  • 在2024年的科技征程中,具身智能的发展已成为全球关注的焦点。从实验室到现实应用,这一领域正以前所未有的速度推进,改写着人类与机器的互动边界。这一年,我们见证了具身智能技术的突破与变革,它不仅落地各行各业,带来新的机遇,更在深刻影响着我们的生活方式和思维方式。随着相关技术的飞速发展,具身智能不再仅仅是一个技术概念,更像是一把神奇的钥匙。身后的众多行业,无论愿意与否,都像是被卷入一场伟大变革浪潮中的船只,注定要被这股汹涌的力量重塑航向。01为什么是具身智能?为什么在中国?最近,中国具身智能行业的进
    艾迈斯欧司朗 2025-02-28 15:45 221浏览
  • 应用趋势与客户需求,AI PC的未来展望随着人工智能(AI)技术的日益成熟,AI PC(人工智能个人电脑)逐渐成为消费者和企业工作中的重要工具。这类产品集成了最新的AI处理器,如NPU、CPU和GPU,并具备许多智能化功能,为用户带来更高效且直观的操作体验。AI PC的目标是提升工作和日常生活的效率,通过深度学习与自然语言处理等技术,实现更流畅的多任务处理、实时翻译、语音助手、图像生成等功能,满足现代用户对生产力和娱乐的双重需求。随着各行各业对数字转型需求的增长,AI PC也开始在各个领域中显示
    百佳泰测试实验室 2025-02-27 14:08 252浏览
  • 美国加州CEC能效跟DOE能效有什么区别?CEC/DOE是什么关系?美国加州CEC能效跟DOE能效有什么区别?CEC/DOE是什么关系?‌美国加州CEC能效认证与美国DOE能效认证在多个方面存在显著差异‌。认证范围和适用地区‌CEC能效认证‌:仅适用于在加利福尼亚州销售的电器产品。CEC认证的范围包括制冷设备、房间空调、中央空调、便携式空调、加热器、热水器、游泳池加热器、卫浴配件、光源、应急灯具、交通信号模块、灯具、洗碗机、洗衣机、干衣机、烹饪器具、电机和压缩机、变压器、外置电源、消费类电子设备
    张工nx808593 2025-02-27 18:04 120浏览
  • 构建巨量的驾驶场景时,测试ADAS和AD系统面临着巨大挑战,如传统的实验设计(Design of Experiments, DoE)方法难以有效覆盖识别驾驶边缘场景案例,但这些边缘案例恰恰是进一步提升自动驾驶系统性能的关键。一、传统解决方案:静态DoE标准的DoE方案旨在系统性地探索场景的参数空间,从而确保能够实现完全的测试覆盖范围。但在边缘案例,比如暴露在潜在安全风险的场景或是ADAS系统性能极限场景时,DoE方案通常会失效,让我们看一些常见的DoE方案:1、网格搜索法(Grid)实现原理:将
    康谋 2025-02-27 10:00 252浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦