You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Cargo实现Rust两库共享私有函数且不对外暴露?

Rust多库间私有函数受控共享的问题解答

问题场景

你需要实现以下需求:

  • 存在两个Rust库:Lib A和Lib B
  • Lib A包含一个公共函数funcA,以及一个仅希望Lib B能访问的内部函数f1
  • 外部组件(M、N、O等)可调用Lib A的公共函数或Lib B的公共函数,但绝对不能直接调用Lib A的f1
  • Lib B作为Lib A的高阶封装,需要内部调用f1来实现自身功能

合法调用示例

Component M -> LibA::pub fn funcA
Component N -> LibB::pub fn funcAA -> LibA::pub fn funcA
Component O -> LibB::pub fn funcAA -> LibA::(受控内部) fn f1

非法调用示例

Component M -> LibA::(受控内部) fn f1

核心结论

直接让两个独立Rust crate(库)共享无pub修饰的私有函数是不可能的——Rust的可见性规则基于crate边界,无pub的函数仅能在定义它的crate内部访问,跨crate完全不可见。不过可以通过以下几种合规的变通方案实现需求,同时保留Lib A和Lib B的API独立性:

方案1:工作区+单crate内模块拆分(推荐)

将Lib A和Lib B放在同一个工作区下,但作为同一个crate内的两个独立模块实现,而非两个单独的crate:

  1. 构建一个根crate,在src/lib.rs中拆分出lib_a和lib_b两个模块
  2. 在lib_a模块中,将f1的可见性设为pub(crate)——这意味着它能被整个crate内的其他模块(包括lib_b)访问,但对外(其他组件)完全不可见
  3. 分别导出lib_a和lib_b的公共API:
// src/lib.rs
pub mod lib_a;
pub mod lib_b;

// src/lib_a.rs
pub fn funcA() { /* 公共逻辑 */ }
pub(crate) fn f1() { /* 仅内部可见的逻辑 */ }

// src/lib_b.rs
use crate::lib_a::{funcA, f1};

pub fn funcAA() {
    // 可合法调用f1
    f1();
    funcA();
}

这种方式既严格隔离了Lib A和Lib B的对外API,又实现了内部函数的安全共享,完全符合Rust的可见性规则,是最推荐的方案。

方案2:带特征控制的内部专用API(折中方案)

如果必须保持Lib A和Lib B为两个独立的crate,可以通过条件编译+隐藏文档的方式,为Lib B开放专用的内部API:

  1. 在Lib A中新增一个internal模块,将f1移入其中,并用#[doc(hidden)]隐藏文档,同时通过feature控制可见性:
// Lib A/src/lib.rs
// 对外公开的公共API
pub fn funcA() { /* ... */ }

// 仅启用`internal`特征时才公开的内部模块
#[cfg(feature = "internal")]
#[doc(hidden)]
pub mod internal {
    pub fn f1() { /* ... */ }
}
  1. 在Lib A的Cargo.toml中声明internal特征:
[features]
internal = []
  1. 在Lib B的Cargo.toml中依赖Lib A时,显式启用internal特征:
[dependencies]
lib_a = { path = "../lib_a", features = ["internal"] }

这样,只有依赖Lib A时启用了internal特征的Lib B能访问lib_a::internal::f1,普通组件若不启用该特征则无法看到这个模块。虽然理论上用户可以手动启用internal特征,但#[doc(hidden)]会在文档中隐藏该模块,明确提示这是内部专用API,不建议外部使用。

方案3:unsafe函数指针(不推荐)

通过在Lib A中导出一个返回f1函数指针的公共函数,让Lib B通过指针调用f1。这种方式绕过了Rust的安全可见性规则,属于unsafe操作,极易引发未定义行为,仅作为极端场景的应急方案:

// Lib A/src/lib.rs
fn f1() { /* ... */ }

pub fn get_f1_ptr() -> unsafe extern "Rust" fn() {
    f1 as unsafe extern "Rust" fn()
}

// Lib B/src/lib.rs
unsafe {
    let f1 = lib_a::get_f1_ptr();
    f1();
}

总结

你的初始猜测方向正确,将两个库合并为同一crate下的独立模块是最安全合规的方案;若必须保持两个独立crate,方案2是兼顾需求与安全性的折中选择。

内容的提问来源于stack exchange,提问作者user2138149

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 11:05:00