news 2026/9/10 12:08:48

comprehensive-rust 教程:深入解析 CXX 的 `[cxx::bridge]` 绑定声明与 Chromium 落地实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
comprehensive-rust 教程:深入解析 CXX 的 `[cxx::bridge]` 绑定声明与 Chromium 落地实践

comprehensive-rust 教程:深入解析 CXX 的#[cxx::bridge]绑定声明与 Chromium 落地实践

【免费下载链接】comprehensive-rustThis is the Rust course used by the Android team at Google. It provides you the material to quickly teach Rust.项目地址: https://gitcode.com/GitHub_Trending/co/comprehensive-rust

CXX 是 Chromium 项目当前采用的 C++/Rust 互操作方案:开发者在一份贴近 Rust 语法的接口定义(#[cxx::bridge]模块)中一次性声明完整的两语言边界,工具链据此在 Rust 与 C++ 两侧生成类型与函数声明。本篇教程围绕src/chromium/interoperability-with-cpp/example-bindings.md展开,从 bridge 模块的语法结构、宏背后的生成机制,到 Rust 切片与std::unique_ptr的跨语言支持,再到 Chromium 中rust_static_library的接入方式与allow_unsafe = true的深层原因,帮助你完整掌握 CXX 绑定的声明、使用与限制。

一、CXX 的核心原则:整条边界必须声明在 bridge 模块中

CXX 对语言边界有极强的约束:整个 C++/Rust 边界必须在.rs源码文件内部的cxx::bridge模块中声明。这意味着你不能像传统手工 FFI 那样,在 Rust 侧写extern "C"、在 C++ 侧写对应的头文件,然后指望两边各凭自觉保持一致;一切由 CXX 工具链统一驱动、统一生成。

CXX 的工作方式可以概括为:你在接口定义语言(interface definition language,形式上与 Rust 高度相似)中描述完整边界,CXX 工具据此在 Rust 和 C++ 两侧同时生成函数与类型的声明。仓库顶层页面 src/chromium/interoperability-with-cpp.md 中配有 CXX 的概览图,展示"同一份接口定义分别生成 C++ 侧代码与 Rust 侧代码、二者再经由最低公共分母的 C ABI 通信"的整体架构:

自动化带来的收益包括:

  • 两侧天然一致:如果#[cxx::bridge]与实际 C++ 或 Rust 定义不匹配,你会直接得到编译错误;而手工维护失同步的绑定只会产生未定义行为(Undefined Behavior)。
  • 自动生成 FFI thunk:为不具备 C 特性的功能(如调用 Rust 方法或 C++ 成员函数)自动生成小的、符合 C ABI 的、自由函数形式的胶水;手工绑定则必须手动编写这类顶层自由函数。
  • 内建核心类型处理:详见下一节。

二、一个完整的 Example Bindings:逐行拆解 bridge 模块

教程文档 src/chromium/interoperability-with-cpp/example-bindings.md 中给出的示例,实际来自仓库内 third_party/cxx/book/snippets.rs 中以cxx_overview锚点标记的代码片段:

#[cxx::bridge] mod ffi { extern "Rust" { type MultiBuf; fn next_chunk(buf: &mut MultiBuf) -> &[u8]; } unsafe extern "C++" { include!("example/include/blobstore.h"); type BlobstoreClient; fn new_blobstore_client() -> UniquePtr<BlobstoreClient>; fn put(self: &BlobstoreClient, buf: &mut MultiBuf) -> Result<u64>; } } // Definitions of Rust types and functions go here

这个片段信息密度很高,值得逐块拆解:

1.#[cxx::bridge]:看起来像普通mod,实际是过程宏

从表面看,mod ffi与普通 Rust 模块毫无二致。但#[cxx::bridge]是一个过程宏(procedural macro),它会对模块体做大量复杂处理:解析extern "Rust"extern "C++"块、校验两侧签名、生成跨语言胶水代码等。生成产物虽然仍然是一个名为ffi的模块,但其内部代码远比表面所见复杂。

2.extern "Rust"块(上半部分):C++ 调用 Rust 的通道

extern "Rust"声明的是Rust 侧类型与函数,供 C++ 调用

  • type MultiBuf;:声明一个不透明类型(opaque type)。其完整定义写在.rs文件里(本例在 bridge 模块之外),C++ 侧只能持有并通过指针/引用使用它,无法看到内部字段。
  • fn next_chunk(buf: &mut MultiBuf) -> &[u8];:声明一个 Rust 函数。注意其签名中使用Rust 切片&[u8]作为返回值——这是 CXX 对 Rust 切片在 C++ 侧的原生支持(见下文"核心类型"小节)。

3.unsafe extern "C++"块(下半部分):Rust 调用 C++ 的通道

extern "C++"声明的是C++ 侧类型与函数,供 Rust 调用,因此标注unsafe(与后续allow_unsafe = true直接相关,详见第四节):

  • include!("example/include/blobstore.h"):告诉 CXX 在生成的头文件中#include哪个 C++ 头文件
  • type BlobstoreClient;:声明 C++ 侧的不透明类型BlobstoreClient,Rust 侧只能通过UniquePtr<BlobstoreClient>之类的智能指针间接持有。
  • fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;:工厂函数,返回原生支持std::unique_ptr的 C++ 智能指针。
  • fn put(self: &BlobstoreClient, buf: &mut MultiBuf) -> Result<u64>;:C++ 成员函数调用,可把&mut MultiBuf(Rust 类型)作为参数传入 C++ 方法。

4. 一个常见误区:头文件并不是被 Rust"解析"

初学者常误以为 CXX 在 Rust 侧解析 C++ 头文件。这是误导性的include!("...")指定的头文件永远不会被 Rust 编译器解释,它只是被原样#include进生成的 C++ 代码中,仅供 C++ 编译器使用。Rust 侧对 C++ 类型的全部认知都来自 bridge 模块中的声明。

三、核心类型支持:std::unique_ptr与 Rust 切片的原生桥接

#[cxx::bridge]示例中透露出两个关键能力,也是 CXX 在 Chromium 被选用的重要原因(详见 src/chromium/interoperability-with-cpp.md 的讲解备注):

能力说明手工绑定的痛点对照
智能指针原生支持std::unique_ptr<T>std::shared_ptr<T>Box<T>可直接跨边界传递手工绑定只能传 C ABI 兼容的裸指针,生命周期与内存安全风险显著增加
Rust 切片原生支持&[T]可跨 FFI 边界传递,即使它不保证特定 ABI 或内存布局std::span<T>/&[T]需要手工拆解为"指针 + 长度"再重建,且两语言对空切片表示不一致,极易出错
字符串差异管理rust::StringCxxString自动处理两语言字符串表示差异例如rust::String::lossy可从非 UTF-8 输入构造 Rust 字符串,rust::String::c_str可为字符串追加 NUL 终止符

仓库中的 third_party/cxx/blobstore 就是一个完整可运行的 CXX 示例工程,其 src/main.rs 展示了比课程片段更完整的桥接声明:

#[allow(unsafe_op_in_unsafe_fn)] #[cxx::bridge(namespace = "org::blobstore")] mod ffi { // Shared structs with fields visible to both languages. struct BlobMetadata { size: usize, tags: Vec<String>, } // Rust types and signatures exposed to C++. extern "Rust" { type MultiBuf; fn next_chunk(buf: &mut MultiBuf) -> &[u8]; } // C++ types and signatures exposed to Rust. unsafe extern "C++" { include!("include/blobstore.h"); type BlobstoreClient; fn new_blobstore_client() -> UniquePtr<BlobstoreClient>; fn put(self: Pin<&mut BlobstoreClient>, parts: &mut MultiBuf) -> u64; fn tag(self: Pin<&mut BlobstoreClient>, blobid: u64, tag: &str); fn metadata(&self, blobid: u64) -> BlobMetadata; } }

与课程示例相比,它额外展示了几个实践要点:

  • #[cxx::bridge(namespace = "org::blobstore")]:通过namespace参数把绑定锚定到 C++ 命名空间,与 include/blobstore.h 中namespace org { namespace blobstore { ... } }的结构对应。
  • 共享结构体(shared structs)struct BlobMetadata声明在模块顶层,字段对两种语言同时可见,C++ 侧可直接按结构体成员使用(见 src/blobstore.cc 中BlobstoreClient::metadata的填充方式)。
  • Pin<&mut BlobstoreClient>:对 C++ 数据的可变引用必须使用Pin。原因是 C++ 数据可能包含自引用指针,不能像 Rust 数据那样随意移动(这一点在 error-handling-qr.md 的讲解备注中也有说明)。
  • Rust 侧定义MultiBuf的真实定义(内部为Vec<Vec<u8>>)与next_chunk的实现都位于 bridge 模块之外的普通 Rust 代码中(src/main.rs)。

对应的 C++ 侧实现 src/blobstore.cc 展示了双向调用的完整闭环:C++ 的put反复调用 Rust 导出的next_chunk(buf)来遍历"不连续文件"的各个分块,直到遇到空切片为止。而 Blobstore 工程的构建文件 BUILD 则展示了 CXX 的 Bazel 接入方式:rust_cxx_bridge规则以src/main.rs为输入生成桥接,cc_library同时依赖blobstore-includebridge/includerust_binary则同时依赖:blobstore-sys:bridge

四、在 Chromium 中接入:rust_static_librarycxx_bindings

把视角拉回 Chromium。根据 src/chromium/interoperability-with-cpp/using-cxx-in-chromium.md,Chromium 中的实践是:为每一个需要使用 Rust 的"叶子节点"(leaf-node)定义一个独立的#[cxx::bridge] mod,通常一个rust_static_library对应一个 bridge 模块。

在既有的rust_static_librarytarget 中,只需在crate_rootsources之外补上两行配置:

cxx_bindings = [ "my_rust_file.rs" ] # list of files containing #[cxx::bridge], not all source files allow_unsafe = true

注意注释里的关键提醒:cxx_bindings列出的是包含#[cxx::bridge]的文件,而不是全部源文件。随后 C++ 头文件会生成在合理位置,C++ 侧直接以"源文件名 +.rs.h"的方式引用:

#include "ui/base/my_rust_file.rs.h"

另外,//base中提供了一些工具函数,用于在 Chromium C++ 类型与 CXX Rust 类型之间转换,例如SpanToRustSlice

为什么需要allow_unsafe = true

这是学生最常问的问题,官方讲解备注给出了两层回答:

  • 宽泛层面:按 Rust 的标准衡量,没有任何 C/C++ 代码是"安全"的。Rust 与 C/C++ 之间的往返调用可能对内存做任意操作,并危及 Rust 自身数据布局的安全性。过多unsafe关键字确实会损害关键字本身的信噪比(这在社区中是有争议的),但严格来说,把任何外来代码引入 Rust 二进制都可能从 Rust 视角引发意外行为。
  • 精确层面:参考 src/chromium/interoperability-with-cpp.md 顶部架构图可知——在幕后,CXX 生成的就是上一节中我们手工编写的unsafeextern "C"函数,因此需要在 GN 侧显式放行。

叶子节点策略与 CXX 的限制

CXX 并非万能。根据 src/chromium/interoperability-with-cpp/limitations-of-cxx.md,CXX 本质上只适用于两种情况:

  • 你的 Rust–C++ 接口足够简单,能够在一份声明中全部描述;
  • 你只使用 CXX 已原生支持的类型,例如std::unique_ptrstd::string&[u8]等。

它存在不少限制,例如不支持 Rust 的Option类型。此外还有若干粘滞点值得讨论:

  • 错误处理基于 C++ 异常(见下一节);
  • 函数指针使用起来很笨拙。

这些限制迫使 Chromium 只在隔离良好的叶子节点中使用 Rust,而不是做任意的 Rust–C++ 互操作。在评估某个 Rust 用例时,一个良好的起点是:先草拟语言边界的 CXX 绑定,看它是否足够简单。补充说明:由于组件构建的链接细节,当前 Chromium 中一个组件的 Rust 代码不能依赖另一个组件的 Rust 代码,这也是把 Rust 限制在叶子节点内的另一个原因。

五、错误处理:Result<T, E>在 Chromium 中的替代方案

CXX 对Result<T, E>的支持依赖于 C++ 异常(详见 src/chromium/interoperability-with-cpp/error-handling.md),因此Chromium 中不能直接使用。官方给出的替代策略如下:

  • T(成功值)的处理
    • 通过输出参数返回,例如&mut T。前提是T能跨 FFI 边界传递,例如:原始类型(如u32usize);或者 CXX 原生支持的类型(如UniquePtr<T>,且要有合适的默认值用于失败情形——Box<T>就做不到)。
    • 保留在 Rust 侧、通过引用暴露。当T是 Rust 类型、既不能跨边界传递、也无法存入UniquePtr<T>时,可能需要这种方式。
  • E(错误值)的处理
    • 布尔值表示成败,如true成功、false失败;
    • 理论上可以保留错误细节,但到目前为止实践中还没有此需求。

仓库还配套了一个真实案例 error-handling-qr.md:Chromium 的 QR 码生成器qr_code_generator就是"用布尔值传达成功/失败、成功结果可跨 FFI 边界传递"的例子:

#[cxx::bridge(namespace = "qr_code_generator")] mod ffi { extern "Rust" { fn generate_qr_code_using_rust( data: &[u8], min_version: i16, out_pixels: Pin<&mut CxxVector<u8>>, out_qr_size: &mut usize, ) -> bool; } }

该示例还有几个值得注意的细节(来自讲解备注):

  • out_qr_size不是向量的大小,而是QR 码的尺寸(即向量大小的平方根,确实略显冗余);
  • 在调用前初始化out_qr_size很重要:与 C++ 不同,在 Rust 中构造一个指向未初始化内存的引用本身就是未定义行为(C++ 要等到实际解引用才算 UB);
  • 关于Pin的疑问:CXX 之所以对 C++ 数据的可变引用要求Pin,是因为 C++ 数据可能包含自引用指针,不能像 Rust 数据那样被移动。

六、从示例到实战:仓库配套资源的阅读路径

若要深入实践,仓库内提供了从简单到完整的配套材料:

资源路径用途
CXX 桥接代码片段合集third_party/cxx/book/snippets.rs本教程示例的来源,含rust_bridgecpp_bridgeshared_typesrust_resultcpp_exceptioncxx_overview等多个锚点
C++ 侧代码片段third_party/cxx/book/snippets.cc配套的 C++ 侧片段
完整可运行示例工程third_party/cxx/blobstore覆盖共享结构体、双向调用、Bazel 构建的完整 CXX 示例
Chromium 互操作章节入口src/chromium/interoperability-with-cpp.mdCXX 架构总览与工具收益说明
CXX 接入 Chromiumsrc/chromium/interoperability-with-cpp/using-cxx-in-chromium.mdGN 配置与allow_unsafe说明
CXX 限制清单src/chromium/interoperability-with-cpp/limitations-of-cxx.md叶子节点策略与类型限制
错误处理替代方案src/chromium/interoperability-with-cpp/error-handling.md、error-handling-qr.mdChromium 中规避 C++ 异常依赖的实践
CXX 架构图src/android/interoperability/cpp/overview.svg同一接口定义双向生成代码的可视化说明

结合本教程与上述资源,你可以完成从"读懂#[cxx::bridge]声明"到"在 Chromium 中为叶子节点配置cxx_bindings并编写第一份 Rust–C++ 绑定"的完整实践路径。核心方法论可归纳为三点:边界全部声明在 bridge 模块内(不要依赖手工 FFI)、只使用 CXX 原生支持的类型、先草拟绑定评估接口是否足够简单再决定是否引入 Rust

【免费下载链接】comprehensive-rustThis is the Rust course used by the Android team at Google. It provides you the material to quickly teach Rust.项目地址: https://gitcode.com/GitHub_Trending/co/comprehensive-rust

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 12:08:37

CodeQwen1.5 离线部署实战教程:开发机断网时怎么跑通本地推理

CodeQwen1.5 离线部署实战教程&#xff1a;开发机断网时怎么跑通本地推理 【免费下载链接】Qwen3-Coder Qwen3-Coder is the code version of Qwen3, the large language model series developed by Qwen team. 项目地址: https://gitcode.com/GitHub_Trending/co/Qwen3-Code…

作者头像 李华
网站建设 2026/9/10 12:07:13

CANN/ge批量构建模型API

aclgrphBundleBuildModel 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 PyTorch、T…

作者头像 李华
网站建设 2026/9/10 12:02:55

AI三天提出人类一年未找到的轨道方案,PSI让新的物理发现工业化

AI自主提出轨道方案一套AI系统基本自主运行三天、消耗约10亿个Token&#xff0c;为一艘计划飞往4.4光年外半人马座阿尔法星系统的航天器&#xff0c;提出了此前人类团队研究一年仍未找到的轨道方案&#xff0c;并通过计算与仿真检验了其可行性。PSI公司亮相与愿景完成这项工作的…

作者头像 李华
网站建设 2026/9/10 12:02:49

基于MyEMS和CNN-LSTM的工业设备故障预测实战

设备故障预测这件事&#xff0c;听起来像是标准的工业4.0叙事&#xff0c;但落到实操层面&#xff0c;大部分团队的现状是&#xff1a;后台屏幕上躺着几十万条SCADA数据&#xff0c;报警规则却还停留在“超过阈值就发短信”的水平。我做这个项目时也是从这一步起步的——把MyEM…

作者头像 李华
网站建设 2026/9/10 12:02:30

LaMa图像修复模型TensorRT加速实战指南

简介&#xff1a;本资源是基于LaMa图像修复模型的TensorRT加速推理Demo工程&#xff0c;面向计算机视觉方向的算法工程师与深度学习部署开发者&#xff0c;解决高分辨率图像修复在边缘端或服务端的低延迟、高性能推理需求。压缩包共264个文件&#xff0c;包含82个TensorRT运行所…

作者头像 李华