Libelektra: 防锈绑定

创建于 2019-05-28  ·  45评论  ·  资料来源: ElektraInitiative/libelektra

大约从 7 月中旬开始,我想为 Elektra 实现 Rust 绑定。

我认为rust-bindgen应该能够自动生成(部分或全部)绑定。 我仍然希望有相当多的手动工作才能使它们正常工作。 根据我目前的理解和@kodebach的评论,这将导致elektra-sys板条箱。
一旦它工作起来,我将在 Rust 中添加一个安全的 API,以便它可以在常规 Rust 中使用而无需调用不安全的代码。 这将是elektra板条箱。
然后我将通过货物测试来确保它们是正确的。

记录 crate 的典型方法是在代码中添加docs.rs将自动构建文档并使其公开可用,因此我认为以这种方式编写文档最有意义。

要将 crate 发布到crates.io ,需要一个带有 API 令牌@markus2330讨论的

我将在项目开始时查看 CMake 集成,因为我目前不熟悉 CMake。

还有什么我应该补充的吗?

最有用的评论

Rust-bindgen 提供了两种生成绑定的方法。 一种是通过命令行,因此这是一个手动过程,如果 C-API 中的任何内容发生变化,则需要重复。 另一种是通过构建脚本,它会在每次 cargo build 运行时运行。 这意味着在每次构建时都会重新生成绑定。 这是目前正在实施的。 但是,它要求使用绑定的每个人都拥有 elekra 所需的必要标头。 我想,如果有人只是安装了 elektra 但没有编译它,他可能无法满足所有必要的要求。 也许每隔一段时间手动重新生成标题更有意义,因为 C-API 不再发生太大变化?

每次构建的再生似乎是正确的解决方案,其他绑定也以这种方式工作(它们需要安装 swig)。 您可以简单地安装生成的头文件,以避免您描述的麻烦。

所有45条评论

我对 Rust 了解不多,但我想rust-bindgen生成的绑定只能在unsafe Rust 中使用? 如果是这种情况,那么最好为那些具有更惯用的 Rust API 的对象封装一个包装器。

AFAIK 大多数 Rust 绑定都有一个*-sys用于 C API 的 1:1 映射的 crate 和另一个带有大多数用户在 Rust 中实际使用的 API 的 crate。 如果有办法告诉 Rust 在必要时自动调用keyDelksDel和朋友,那就太好了。

如果是这种情况,那么最好为那些具有更惯用的 Rust API 的对象封装一个包装器。

是的,这就是计划。 在这样做的同时,还要与 C API 进行比较,也许会发现 C API 的改进(至少在文档中)。

@PhilippGackstatter正如所讨论的:还请了解如何上传到https://crates.io/以及如何将绑定集成到我们的 CMake 系统中。

@PhilippGackstatter 有什么进展吗? 我们有人可能也对扩展 Rust 绑定感兴趣。

@markus2330我是几天前开始的,但我主要是在阅读 bindgen、cmake 以及如何将它集成到项目中,所以没有什么可展示的。 但我现在已经准备好了前几件事(见#2826)。 我现在正在全力投入这个项目。

回答 #2826 中的问题(请更喜欢在问题中提问,因为 PR 倾向于混淆与代码不直接相关的讨论):

一个是,我需要为 src/include 中的哪些头文件生成绑定。 至少是 kdb.h,但是在没有插件支持的情况下,我还需要其他低级 API 吗?

不,低级 API 仅在 kdb.h 中

另一个是,我是否必须更改所有 docker 脚本才能安装 rustup(用于安装 cargo 和 rustc)?

是的,您需要更改 docker 脚本,也许还需要更改 Jenkinsfile。 但是您不需要更改所有这些,如果您为最近的发行版构建就足够了。

如果您也可以使用 Debian Buster 中捆绑的原生 Rust 编译它,那就太好了。 Debian Buster docker 文件尚未合并 #2819

我想没有自动化的方法可以做到这一点。

有一些想法可以做到这一点:#730

Rust-bindgen 提供了两种生成绑定的方法。 一种是通过命令行,因此这是一个手动过程,如果 C-API 中的任何内容发生变化,则需要重复。 另一种是通过构建脚本,它会在每次 cargo build 运行时运行。 这意味着在每次构建时都会重新生成绑定。 这是目前正在实施的。 但是,它要求使用绑定的每个人都拥有 elekra 所需的必要标头。 我想,如果有人只是安装了 elektra 但没有编译它,他可能无法满足所有必要的要求。 也许每隔一段时间手动重新生成标题更有意义,因为 C-API 不再发生太大变化?

每次构建的再生似乎是正确的解决方案,其他绑定也以这种方式工作(它们需要安装 swig)。 您可以简单地安装生成的头文件,以避免您描述的麻烦。

是的,这就是计划。 在这样做的同时,还要与 C API 进行比较,也许会发现 C API 的改进(至少在文档中)。

到目前为止,我发现了一些小的改进机会

  • keyGetBinary :作为调用代码,我不知道 -1 的返回值是否意味着maxSize is 0type mismatch ,或其他什么。 由于 Rust 在其返回参数中使用显式错误处理,我希望能够将类型不匹配与错误匹配,并将“maxSize 相关”错误与另一个匹配。 但目前我必须使用更通用的错误。 我可以自己检查类型不匹配,但是keyGetBinary会这样做,所以我进行了两次相同的检查。
    keySetName做了类似的事情,将两个不同的错误匹配到 -1。 在这两种情况下,都存在错误(无效名称)和声音程序中可能发生的错误(键已经在键集中),所以我可以理解这个决定。 但是为什么不使用 -2 来明确和避免双重检查呢?
  • 从语法上讲, keyIsDirectBelow应该是keyIsDirectlyBelow 🙂 吗? 如果是这样,我应该在 Rust API 中纠正这个问题吗?

另一个问题: keyRel未在 CPP 绑定中实现。 我也应该在 Rust 中省略这个吗?

很棒的工作,从您的问题中可以明显看出您已经深入研究了 API。

我可以自己检查类型不匹配,但是 keyGetBinary 会这样做,所以我进行了两次相同的检查。

也许您甚至可以使用类型系统来避免错误调用? (keyGetBinary 只允许用于二进制键)

但是为什么不使用 -2 来明确和避免双重检查呢?

原因是兼容性:API 最初只返回 -1,并且不可能在不破坏现有程序的情况下添加其他错误代码(可能有==-1来检查错误)。 但是在下一个版本 (0.9) 中,我们可以再次破坏 API。 我们可以通过声明任何低于 0 的值表示错误来避免兼容性问题。 我完全同意绑定应该产生精确的错误。

您想修复这些 API 问题吗?

如果是这样,我应该在 Rust API 中纠正这个问题吗?

API 不应在拼写上有所不同。 如果我们修复它,我们应该在C API和所有绑定中修复它(实际上只有Java和Go需要手动适配,其他的无论如何都会正确重新生成)。

keyRel 未在 CPP 绑定中实现。 我也应该在 Rust 中省略这个吗?

是的,您可能已经注意到 keyIs(Direct)Below 和 keyRel 具有重叠功能。 keyRel 的想法是保持 API 小(因此库小)。 但是 keyRel 原样不可用,而且速度也很慢。 所以我们很可能会在 0.9 内删除它。 有关要删除的其他候选人,请参阅 doc/todo/FUTURE。

也许您甚至可以使用类型系统来避免错误调用? (keyGetBinary 只允许用于二进制键)

好主意啊。 我可以有BinaryKeyStringKey ,只有第一个会有get_binary()方法,只有第二个会有get_string()方法,依此类推。 我会调查这个。

您想修复这些 API 问题吗?

我可以做到。 取决于我应该优先考虑什么。 在完成了 Rust 的安全 API 之后,你说在 Rust 中也有插件 API 会很好。 您可以决定什么更重要。

好主意啊。 我可以有 BinaryKey 和 StringKey,只有第一个会有 get_binary() 方法,只有第二个会有 get_string() 方法,依此类推。 我会调查这个。

谢谢你。 它可能需要太多的演员表,所以让我们看看这是否是一个好主意。 另一种类型可能对键集中的键有用(其中 setName 是不允许的)。

我可以做到。 取决于我应该优先考虑什么。 在完成了 Rust 的安全 API 之后,你说在 Rust 中也有插件 API 会很好。 您可以决定什么更重要。

是的,首先从 kdb.h 完成 Rust API,然后我们将看到我们还需要花费多少小时。

IMO Rust 绑定(以及任何与此相关的绑定)应该有两个版本。 一个尽可能接近地反映 C API,另一个更符合基于第一个版本的语言的习惯。 在惯用的版本中,使用带有BinaryKeyStringKey (甚至泛型)的类型系统可能是一个好主意,如果它可以更轻松地使用 Rust 的 API。

@kodebach我同意。 这似乎也是用 elektra 和 elektra-sys 板条箱完成的。

@kodebach我同意。 这似乎也是用 elektra 和 elektra-sys 板条箱完成的。

是的,我也这么认为。 如果安全的 Rust API 有需要解决的限制,可以导入elektra_sys并直接调用一对一的 C 绑定函数。

谢谢你。 它可能需要太多的演员表,所以让我们看看这是否是一个好主意。 另一种类型可能对键集中的键有用(其中 setName 是不允许的)。

它非常适合关键实施。 但是对于 KeySet,我遇到了障碍。 对于任何返回 Key 的方法,它必须符合我创建的通用接口。 我已经实现了一个具有通用返回参数的方法get_value 。 对于 BinaryKeys 是字节,对于 StringKeys 是字符串。 但是ksNext的 Rust 版本现在返回什么? 一个满足“关键接口”的对象,但具有什么价值? 我必须选一个。
这就是签名的样子,其中 Value 是get_value返回的类型。 我只能指定字节( Vec<u8> )或字符串。
pub fn next(&mut self) -> Box<dyn WriteableKey<Value = Vec<u8>>>;

所以我可以将它统一为字节,但是用户必须自己转换为字符串。 由于 StringKey 和 BinaryKeys 的唯一区别是它们的set_valueget_value实现,因此此更改将消除这种显式性,我基本上又只有 Keys 了。

我想真正的问题是当前实现中的 KeySet 并没有明确说明它包含什么类型的键,但是 *keys 是。 但是我认为允许 KeySet 的实例只包含 StringKey 或 BinaryKey 是一个太大的限制。
我认为 Key 和 KeySet 都必须明确说明它们包含的内容或不包含任何内容。 我现在倾向于通用 Key 和 KeySet,只是为了与 elektra 的其余部分保持一致。
有什么想法吗?

从可用性的角度来看,返回具有字符串 getter 的 Key 是有意义的,因为这是最常用的变体。 应该禁用名称的设置器(如果可能的话),因为我们已经知道这个键(由 next 返回)是 KeySet 的一部分。

一般来说,类型系统应该支持用户,而不是妨碍用户。 所以尽量保持简单。 最常见的错误是:

  1. 试图更改密钥集中的密钥的密钥名称。
  2. 尝试更改元数据键(或其他常量键)。
  3. 令人困惑的 Key/KeySet 重复和对它们的引用。
  4. 对 KeySet 进行迭代,这些 KeySet 也用于以迭代不再正常工作的方式剪切键。
  5. 忘记释放 Key/KeySet

所以如果类型系统可以在那里提供帮助,那就太好了。 5. 希望在设计上是不可能的,对于 1. 和 2. 您的抽象应该有所帮助。

二进制/字符串混淆实际上是一个相当罕见的错误(因为二进制键非常不典型:主要用于保存函数指针)。

顺便提一句。 如果您想编写有关 API 安全使用的设计决策,请继续(文档/决策)

从可用性的角度来看,返回具有字符串 getter 的 Key 是有意义的,因为这是最常用的变体。

但并不是每个字节序列都是有效的 UTF-8,所以它不再是真正的类型安全了,是吗?

AFAIK Rust 中的宏系统非常强大,也许有一种方法可以编写一个始终返回正确类型的函数。 例如,在 Kotlin 中,有一种映射技术可以对键中的值类型进行编码。 此处的 API 参考是一个示例。

或者,只接受StringKeyStringKeySet可能是有意义的,因为二进制密钥非常罕见,而且大多不在配置中使用。

从可用性的角度来看,返回具有字符串 getter 的 Key 是有意义的,因为这是最常用的变体。 应该禁用名称的设置器(如果可能的话),因为我们已经知道这个键(由 next 返回)是 KeySet 的一部分。

但是,虽然很少见,但仍然有可能使用混合键的 KeySet。 然后总是返回一个 StringKey 并调用get_string将是一个错误,但类型系统不仅允许它而且引导你走向它,因为该类型没有get_binary方法。

在这样做之前,如果用户确定里面只有 StringKeys(对于那些不是来自 Rust 的 KeySet),我建议使 KeySet 通用并将其实例化为KeySet<StringKey> 。 那么很自然地迭代它只会产生 StringKeys。
它还将通过类型系统强制 KeySet 是同质的,至少那些由 Rust 用户创建的,这将总体上更安全。
在需要二进制密钥的极少数情况下,用户必须检查is_binaryis_string然后进行转换,这将是一个安全的方法调用。

令人困惑的 Key/KeySet 重复和对它们的引用。

我认为我唯一能做的就是促进使用重复而不是引用计数。 性能可能更差,但 keyDel 是由 Rust 自动调用的,而 ref 计数是完全手动的。 所以重复肯定比引用计数更容易正确。

对 KeySet 进行迭代,这些 KeySet 也用于以迭代不再正常工作的方式剪切键。

你的意思是在迭代时修改键集?

顺便提一句。 如果您想编写有关 API 安全使用的设计决策,请继续(文档/决策)

那会是什么内容呢?

AFAIK Rust 中的宏系统非常强大,也许有一种方法可以编写一个始终返回正确类型的函数。

我认为可以包含任何内容和具体 Key 类型的 KeySet 的“当前”设计不能协同工作,至少不能很好地协同工作。 但我会研究宏。

但并不是每个字节序列都是有效的 UTF-8,所以它不再是真正的类型安全了,是吗?

Elektra 的字符串或二进制值都不需要是 UTF-8。 Elektra 只在字符串和二进制之间做出决定(可能包含 0 个字节)。

AFAIK Rust 中的宏系统非常强大,也许有一种方法可以编写一个始终返回正确类型的函数。 例如,在 Kotlin 中,有一种映射技术可以对键中的值类型进行编码。 此处的 API 参考是一个示例。

我们还需要注意将精力放在有用的功能上。 二元键很少见。

或者,只接受 StringKeys 的 StringKeySet 可能是有意义的,因为二进制键非常罕见,而且大多不在配置中使用。

是的,但我会将 StringKeySet 视为普通的 KeySet。

但是,虽然很少见,但仍然有可能使用混合键的 KeySet。 然后总是返回一个 StringKey 并调用 get_string 将是一个错误,但类型系统不仅允许它而且引导你走向它,因为该类型没有 get_binary 方法。

是的,但正如所说,这是一个小问题。 存储二进制数据(如函数地址)的人会发现如何转换密钥(如果有一些关于它的文档)。

在这样做之前,我建议使 KeySet 通用并将其实例化为 KeySet如果用户确定里面只有 StringKeys(对于那些不是来自 Rust 的 KeySet)。 那么很自然地迭代它只会产生 StringKeys。

这只是假安全,因为 KeySet 来自 KDB(外部)并且它们在任何情况下都可能包含二进制数据。 因此,我更喜欢 KeySet 非通用。

如果您想使用泛型,请提供将 KeySet 转换为(泛型)数据结构的 getter 和 setter。 例如,一个 Elektra 整数数组到Vec<i32>

你的意思是在迭代时修改键集?

cut修改 KeySet。 一般来说,迭代器这样做是安全的,但很多人在做对时遇到问题。

那会是什么内容呢?

我们在这里讨论的内容以及您如何设计它的摘要。

我认为可以包含任何内容和具体 Key 类型的 KeySet 的“当前”设计不能协同工作,至少不能很好地协同工作。

我同意。

但我会研究宏。

请不要把它放在首位。

Elektra 的字符串或二进制值都不需要是 UTF-8。

那么我们应该根据快速搜索使用OsStringCString而不是String

Elektra 的字符串或二进制值都不需要是 UTF-8。

那么我们应该根据快速搜索使用OsStringCString而不是String

现在,我正在将 Rust 的String (UTF-8)转换为CString然后再将其传递给 elektra。 基本原理是, String是默认字符串,大多数其他库都希望使用它。
我可以让高级 API 请求并返回CString s,这样用户就必须处理转换代码,如果他们需要String 。 这将使 API 更薄,需要处理的错误处理更少。 我想这归结为大多数用户希望如何使用 API,我对此没有太多了解。

我同意最好返回语言最常见的 String 类型。 非 UTF8 字符串应该很少见(可能比二进制文件更少见)。

我同意最好返回语言最常见的 String 类型。 非 UTF8 字符串应该很少见(可能比二进制文件更少见)。

我试图找出处理 Rust -> C 方向的最佳方法,从 UTF-8 到 C 字符串。 UTF-8 字符串允许包含零字节,但出现的唯一代码点是 NUL 字符,否则. 我认为在绑定文档中将其声明为前提条件是合理的,即字符串不允许包含零字节。 如果它仍然存在,那么代码会在那时发生恐慌。

另一种可能性是从所有接受字符串的集合函数中返回错误。 但是用户必须一直处理这个NulError并且它实际上甚至永远不会返回。

keyNew可以在分配错误时返回空指针。 在 Rust 中,我可以返回显式错误或恐慌,但不能返回隐式 null。 关于信号错误的 Rust中止。 java 绑定似乎无法处理这种情况,因此我认为它也会退出进程,因为抛出了NullPointerException
您是否同意在此处调用panic是最好的(abort 不允许析构函数运行)?

我认为在绑定文档中将其声明为前提条件是合理的,即字符串不允许包含零字节。 如果它仍然存在,那么代码会在那时发生恐慌。

是的,是有道理的。

您是否同意在此处调用 panic 是最好的(abort 不允许析构函数运行)?

是的,如果 malloc 失败,恐慌是有道理的。 (在 Rust 的情况下,stdlib 也会做同样的事情。在 C 中,stdlib 不会中止,因此 C-Elektra 也不会中止)。

现在 Rust 绑定已合并到 master,我想将它们发布到 crates.io。
我建议发布它们的版本设置为(默认) 0.1.0 ,而不是将其绑定到 elektra,仅仅因为绑定的成熟度和 elektra 本身不同。 你同意@markus2330 吗?

https://crates.io上发布需要一个 GitHub 帐户。 板条箱的所有权可以在帐户之间转移,所以我现在可以使用我自己的。 或者其他人可以登录并向我发送发布所需的 API 令牌。

感谢您将它们发布到 crates.io :sparkle:

我会将版本直接绑定到 Elektra,因为它们是 Elektra 存储库的一部分。 但这并不重要:如果 crates.io 通常有一些特定的版本模式,最好坚持那里常见的内容。

或者其他人可以登录并向我发送发布所需的 API 令牌。

我登录了一个授权。 我会向您发送 API 令牌。

我会将版本直接绑定到 Elektra,因为它们是 Elektra 存储库的一部分。 但这并不重要:如果 crates.io 通常有一些特定的版本模式,最好坚持那里常见的内容。

这样做的主要问题是,根据语义版本控制,如果我们必须对绑定进行重大更改,我们不能仅仅相应地升级版本。 因此,我们只能在 elektra 进行更改时进行重大更改。

根据语义版本控制,只要版本以0开头,您就可以进行任何重大更改。 如果我们发布 Elektra 1.0 ,我们也有兴趣保持绑定稳定。 即使我们没有这样做,我们将来也可以选择使版本独立(只需增加 Rust 绑定的主要版本)。 所以我认为现在简单地使用 Elektra 的版本是安全的。

你说得对,我没有想过以后增加主要版本。

现在, wrapper.h指定#include "kdb.h"以包含 kdb 标头并为其生成绑定。 但是 clang 找不到标题(例如在ubuntu:18.10 )。 所以我必须明确告诉 clang 包含/usr/include/elektra才能构建它。
elektra 是否始终安装在/usr/include/elektra以便此解决方案适用于大多数发行版?

elektra 是否始终安装在 /usr/include/elektra 中,以便此解决方案适用于大多数发行版?

是的,因为还有另一个库(我认为来自 Kerberos)使用/usr/include/kdb.h

现在/usr/include/elektra必须是包含路径的一部分,但 AFAIK 我们想改变它,以便可以使用#include <elektra/kdb.h>代替。

elektra 是否始终安装在 /usr/include/elektra 中,以便此解决方案适用于大多数发行版?

默认情况下它是 /usr/local/include/elektra 但大多数发行版将使用 /usr/include/elektra 但不能保证。 这就是为什么构建系统通常支持定位头文件的原因。 Elektra 支持 cmake 和 pkg-config。

你能提供一些关于你需要它的地方的背景吗?

可以代替。

必须改用。 相关 PR 是 #2880

更改为#include <elektra/kdb.h>在 Ubuntu 中肯定有效。 那么我将更改为该路径,而不是包含/usr/include/elektra

更改为#include <elektra/kdb.h>在 Ubuntu 中肯定有效。 那么我将更改为该路径,而不是包含/usr/include/elektra

它可能不适用于所有标题,有些依赖于/usr/include/elektra位于包含路径中。

可能最好的办法(如果可能的话)是使用pkg-configcmake --find-package来查找 Elektra 文件(IMO cmake效果更好)。

你能提供一些关于你需要它的地方的背景吗?

所以如果用户在他的机器上编译 elektra,那么他只需要 rust/cargo 并且可以使用绑定。 但是另一个用例,crates.io 应该用于,如果有人通过他们的包管理器安装 elektra(和头文件)。 然后库和头文件可用。 现在该用户在他们的依赖项中包含了 elektra,cargo 将获取elektra-sys板条箱。 它仅依赖于构建脚本和 clang 来生成绑定。 但是 clang 需要以某种方式找到kdb.h 。 所以我可以在构建脚本中传递额外的硬编码包含路径,也可以直接修改#include ...语句。

可能最好的办法(如果可能的话)是使用pkg-configcmake --find-package来查找 Elektra 文件(IMO cmake效果更好)。

我可以尝试添加 pkg-config 或 cmake 作为构建依赖项,并通过这种方式找到kdb.h 。 我会调查这个。 我同意这是最可靠的方式。

是的,您可以尝试在构建脚本中调用 pkg-config。 如果 pkg-config 不可用,您可以尝试硬编码路径,如 /usr/include/elektra 和 /usr/local/include/elektra。 (如果 crates.io 不需要 pkg-config 可用。)

你可以试试这个板条箱

是的,您可以尝试在构建脚本中调用 pkg-config。 如果 pkg-config 不可用,您可以尝试硬编码路径,如 /usr/include/elektra 和 /usr/local/include/elektra。 (如果 crates.io 不需要 pkg-config 可用。)

我添加了pkg-config作为可选依赖项。 如果添加了它,它将搜索 elektra 并使用提供的 includeir。 否则它将在您命名的两个目录中搜索。

绑定现已发布: elektraelektra-sys :smiley:

由于在 docs.rs 构建环境中缺少 libelektra 的系统依赖性,文档没有构建。 此外,他们将在 9 月 30 日更改构建环境。
我提交了添加 libelektra 作为依赖项的请求,以便它能够在 9 月 30 日正确构建。 他还将软件包添加到现有环境中,因此文档现在可用:+1:

我觉得在#2980合并之后,这个issue可以关闭了。

非常好,他们反应超快。 他们用相当老的 libelektra 构建它会不会有问题? (我没有检查哪个版本,但如果他们从包管理器中包含它,它肯定会比 0.9 旧。)

elektra板条箱没有问题,因为它只是文档。 elektra-sys我认为包含它生成的任何版本的 elektra 而不是当前版本。 但我认为几乎没有人会使用原始绑定而不是包装器。 因此,自动构建文档是一个小小的折衷。

然后您可以在我们的 repo 中添加指向文档的链接吗?

无论如何,elektra-sys 似乎几乎无法使用。 它显示了数百个与 Elektra 无关/几乎没有关系的符号。 此外,没有针对各个功能的文档,例如

https://docs.rs/elektra-sys/0.9.0/elektra_sys/fn.keyString.html

然后您可以在我们的 repo 中添加指向文档的链接吗?

是的,我会将它添加到现有的 PR 中

无论如何,elektra-sys 似乎几乎无法使用。 它显示了数百个与 Elektra 无关/几乎没有关系的符号。

我会调查这个。

此外,没有针对各个功能的文档,例如

-sys crates 通常没有文档(例如openssl-sys ),因为它们是 C 等效项的一对一翻译。 所以必须直接查找C doc。 我还必须手动复制所有文档,这又增加了维护负担。 不过,我可以在主文档页面上链接到https://doc.libelektra.org/api/current/html/index.html

无论如何,elektra-sys 似乎几乎无法使用。 它显示了数百个与 Elektra 无关/几乎没有关系的符号。

我会调查这个。

它已在 #2980 中修复,并将在下次我们发布 crate 时修复在 docs.rs 上。

不过,我可以在主文档页面上链接到https://doc.libelektra.org/api/current/html/index.html

是的好主意!

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

markus2330 picture markus2330  ·  4评论

sanssecours picture sanssecours  ·  3评论

markus2330 picture markus2330  ·  4评论

markus2330 picture markus2330  ·  4评论

mpranj picture mpranj  ·  3评论