我正在rust中编写一个库,它公开了一个使用私有模块的函数。现在,我可以使用以下命令呈现这个私有模块的文档:
cargo doc --document-private-items
现在,我有一个名为reader
的模块,它包含一个结构,其中包含一些函数。模块本身是使用mod reader
从lib.rs
文件导入的,其类型如下:use reader::BufferedOpenTypeFontReader;
现在,我想向BufferedOpenTypeFontReader
结构添加一些文档,所以这就是它的实现:
/// A buffered `&[u8]` reader for reading an OpenType Font (OTF).
pub struct BufferedOpenTypeFontReader<'a> {
reader: BufReader<&'a [u8]>,
}
//# The basic implementation of the `OpenTypeFontByteSliceReader` struct.
impl<'a> BufferedOpenTypeFontReader<'a> {
//# Defines the constants which are required in this `impl` block.
const U32_SIZE: usize = 4; // The amount of bytes in a `u32`.
/// Create a new `BufferedOpenTypeFontReader<'a>` implementation which reads from the given `&[u8]`.
///
/// # Examples:
/// ```
/// use otfdecode::reader::BufferedOpenTypeFontReader;
///
/// let otf_bytes = [];
/// let otf_reader = BufferedOpenTypeFontReader::new(otf_bytes);
/// ```
pub fn new(data: &'a [u8]) -> Self {
Self {
reader: BufReader::new(data),
}
}
pub fn read_u32(&mut self) -> u32 {
let mut buffer = [0u8; Self::U32_SIZE];
self.reader.read_exact(&mut buffer).unwrap();
u32::from_be_bytes(buffer)
}
}
但是,当我运行cargo test
时,我会遇到以下问题:
failures:
---- srcreader.rs - reader::BufferedOpenTypeFontReader::new (line 42) stdout ----
error[E0603]: module `reader` is private
--> srcreader.rs:43:16
|
4 | use otfdecode::reader::BufferedOpenTypeFontReader;
| ^^^^^^ private module
|
note: the module `reader` is defined here
--> C:UserskevinDevelopmentgithub.comkdeconinckOTFDecodeappsrclib.rs:30:1
|
30 | mod reader;
|
如何在不公开模块reader
的情况下为函数new
编写文档测试?
来自文档测试文档:
请注意,它们仍然只会链接到您机箱中的公共物品;如果你需要测试私有项目,你需要编写一个单元测试。https://doc.rust-lang.org/rustdoc/documentation-tests.html
在撰写本文时,这在Rust中是不可能的。我对这个问题做了一些研究,以下是我的结论:
为什么这是一个问题
文档测试被编译为独立的外部单元。首先编译库,然后将doctest编译单元链接到库。编译doctest时,库的行为与外部库完全相同。即使是pub(crate)
也不起作用,因为根据编译器的说法,它实际上是一个单独的库。
如果你把文档看作是一种帮助人们使用你的图书馆的产品,这在哲学上是有意义的。他们将使用你的库作为外部,所以让库的行为与他们一样是有意义的。这样做的缺点是,它有点阻碍为其他目的编写(好的(文档:从长远来看,使库本身更容易维护。
有一些声音想要改变这种状况。特别是,请参阅此问题。但在撰写本文时,它还没有得到太多的关注。
解决方法
真的没有好的解决办法。这些都是人们想出的变通办法,但没有一个是令人满意的:
- 不要编写使用私有符号的文档测试。相反,使用单元测试。然而,这通常会使您的文档变得更糟
- 将
ignore
添加到使用专用符号的doctest中。这样做的缺点是,如果您以后进行更改,您不确定文档化的示例是否仍然有效(除非您将代码复制到单元测试中,但随后您有重复的代码(。就像这样:
/// Computes the sum of two numbers.
///
/// # Examples
/// ```ignore
/// use calculator::adder::private_adder;
/// assert_eq!(5, private_adder(2, 3));
/// ```
fn private_adder(left: i32, right: i32) { left + right }
- 让你的符号全部公开,但"隐藏";它位于
detail
或private
子模块下,这样库的用户就可以清楚地知道不应该使用它。这样做的缺点是,它仍然允许库的用户发现和使用这些功能,认为它们很聪明,然后可能会破坏东西。它还扰乱了您的公共API,并且如果您关心的是库内部的秘密业务逻辑,则可能会揭示更多信息 - 有一个名为"可见性"的机箱,允许您根据特征更改符号的可见性。使用此功能,如果启用了该功能,则可以将每个专用符号公开。然后,您可以通过在命令中添加
doctest_privates
功能来编译doctest:cargo test --doc --features doctest_privates
。然而,这需要将以下属性添加到您的每个私有符号(或者至少添加到您希望在doctest中使用的所有模块、类和函数(:
/// Computes the sum of two numbers.
///
/// # Examples
/// ```
/// use calculator::adder::private_adder;
/// assert_eq!(5, private_adder(2, 3));
/// ```
#[cfg_attr(feature = "document_privates", visibility::make(pub))]
fn private_adder(left: i32, right: i32) { left + right }