互操作性

类型应尽早实现常见的 trait(C-COMMON-TRAITS)

Rust 的 trait 系统不允许存在“孤儿”:大致上,每个 impl 必须存在于定义该 trait 的 crate 或实现该类型的 crate 中。因此,定义新类型的 crate 应该尽早实现所有适用的常见 trait。

为了理解这一点,请考虑以下情况:

  • Crate std 定义了 trait Display
  • Crate url 定义了类型 Url,但没有实现 Display
  • Crate webappstdurl 中导入了内容。

由于 webapp 既不定义 Display 也不定义 Url,因此它无法为 Url 添加 Display。 (注意:newtype 模式可以提供一种有效但不便的解决方法。)

最重要的常见 trait 实现来自 std

请注意,通常类型同时实现 Default 和空的 new 构造函数是常见且预期的。new 是 Rust 中的构造函数惯例,用户希望它存在,所以如果基本构造函数不需要参数,那么即使它与 default 功能相同,也应该存在。

转换使用标准的 FromAsRefAsMut trait(C-CONV-TRAITS)

在合适的情况下,应该实现以下转换 trait:

以下转换 trait 不应被实现:

这些 trait 基于 FromTryFrom 提供了通用实现。应优先实现这些。

标准库中的示例

  • From<u16> 实现了 u32,因为较小的整数总是可以转换为较大的整数。
  • From<u32> 并未为 u16 实现,因为如果整数太大,转换可能无法完成。
  • TryFrom<u32>u16 实现,当整数太大而无法容纳在 u16 中时返回错误。
  • From<Ipv6Addr> 实现了 IpAddr,它是一种可以表示 v4 和 v6 IP 地址的类型。

集合实现 FromIteratorExtend(C-COLLECT)

FromIteratorExtend 使集合可以方便地与以下迭代器方法一起使用:

FromIterator 用于创建一个包含迭代器中项的新集合,而 Extend 则用于将迭代器中的项添加到现有集合中。

标准库中的示例

  • Vec<T> 实现了 FromIterator<T>Extend<T>

数据结构实现 Serde 的 SerializeDeserialize(C-SERDE)

扮演数据结构角色的类型应实现 SerializeDeserialize

从数据结构到非数据结构存在一个连续体,其中有一部分是灰色区域。LinkedHashMapIpAddr 是数据结构。有人希望从 JSON 文件中读取 LinkedHashMapIpAddr,或者通过 IPC 将其发送到另一个进程,这是完全合理的。而 LittleEndian 不是数据结构。它是 byteorder crate 使用的一种标记,用于在编译时针对特定字节顺序进行优化,实际上 LittleEndian 的实例在运行时永远不会存在。这些是明确的例子;#rust 或 #serde IRC 频道可以帮助评估更多模棱两可的情况。

如果一个 crate 没有因为其他原因依赖于 Serde,它可以选择通过 Cargo 配置选项来控制 Serde 的实现。这样,下游库只需要在需要这些实现时支付编译 Serde 的代价。

为了与其他基于 Serde 的库保持一致,Cargo 配置的名称应为 "serde"。不要使用 serde_implsserde_serialization 等不同的名称。

在不使用派生的情况下,规范的实现看起来像这样:

[dependencies]
serde = { version = "1.0", optional = true }
#![allow(unused)]
fn main() {
pub struct T { /* ... */ }

#[cfg(feature = "serde")]
impl Serialize for T { /* ... */ }

#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for T { /* ... */ }
}

使用派生时:

[dependencies]
serde = { version = "1.0", optional = true, features = ["derive"] }
#![allow(unused)]
fn main() {
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct T { /* ... */ }
}

类型在可能的情况下实现 SendSync(C-SEND-SYNC)

当编译器确定合适时,SendSync 会自动实现。

在操作原始指针的类型中,要谨慎确保你的类型的 SendSync 状态准确反映其线程安全特性。类似于以下的测试可以帮助捕捉类型是否实现 SendSync 的非预期回归。

#![allow(unused)]
fn main() {
#[test]
fn test_send() {
    fn assert_send<T: Send>() {}
    assert_send::<MyStrangeType>();
}

#[test]
fn test_sync() {
    fn assert_sync<T: Sync>() {}
    assert_sync::<MyStrangeType>();
}
}

错误类型应具有意义且表现良好(C-GOOD-ERR)

错误类型是任何 Result<T, E> 中的 E 类型,它由你的 crate 的任何公共函数返回。错误类型应始终实现 std::error::Error trait,这是错误处理库如 error-chain 抽象不同错误类型的机制,并且允许错误作为另一个错误的 source()

此外,错误类型应实现 SendSync trait