#traits #macro #impl #test-macro #testing

no-std dev impls

判断类型是否实现了逻辑特性表达式

4个稳定版本

使用旧Rust 2015

1.0.3 2019年12月31日
1.0.1 2019年12月30日

Rust模式 中排名 #614

Download history 803/week @ 2024-03-14 917/week @ 2024-03-21 793/week @ 2024-03-28 1007/week @ 2024-04-04 1178/week @ 2024-04-11 1458/week @ 2024-04-18 1255/week @ 2024-04-25 1091/week @ 2024-05-02 1098/week @ 2024-05-09 1006/week @ 2024-05-16 827/week @ 2024-05-23 794/week @ 2024-05-30 1125/week @ 2024-06-06 716/week @ 2024-06-13 1028/week @ 2024-06-20 943/week @ 2024-06-27

每月下载量 3,955
19 crates 中使用

MIT/Apache

42KB
263


判断类型是否实现了逻辑特性表达式?,由 @NikolaiVazquez 提供!

此库定义了 impls!?,它返回一个 bool 值,表示类型是否在一系列特性?上实现了布尔表达式。

assert!(impls!(String: Clone & !Copy & Send & Sync));

请参阅“示例”以了解详细用法,如果您够勇敢,请参阅“特性相关类型大小”以查看一些令人诅咒的代码。

索引

推理

作为一个库的作者,确保你的API保持稳定非常重要。特性实现是API稳定性的一个部分。例如:如果你不小心引入了一个内部类型,使你的公开类型不再是 SendSync,你现在已经破坏了你的API而没有注意到!这种情况最常见的情况是添加一个 原始指针(即 *const T*mut T)作为类型字段。

通过使用 impls! 检查这种情况,无论是在 编译时间 还是在单元测试中,你可以在太晚之前确保没有做出破坏API的更改。

用法

此软件包可在 crates.io 上获得,并可以通过将以下内容添加到项目的 Cargo.toml 中来使用

[dependencies]
impls = "1"

并添加以下内容到你的crate根目录(main.rslib.rs

#[macro_use]
extern crate impls;

当使用Rust 2018 版本时,如果不想使用#[macro_use],下面的导入可以提供帮助。

use impls::impls;

词汇表

本文档使用了一些术语,这些术语可能对经验不足的 Rust 用户来说是新的。本节旨在使这些术语更容易理解。如果您对这些术语已经熟悉,可以跳过本节。

在 Rust 中,宏是作用在抽象语法树(AST)上的函数。它们通过一系列规则对这些输入标记进行操作,将它们映射到输出标记。正因为如此,只有它们的输出才会被类型检查。

如果您想了解宏的实现,我推荐以下书籍:

使用这个包,您不需要了解宏是如何定义的。

特性

在 Rust 中,特质是一种定义通用属性的方式。应该将其理解为表达类型能做什么。例如:如果一个类型为某些类型 T 实现了 Into,那么我们知道可以通过调用它的 .into() 方法将其转换为 T

如果您想详细了解特质,我推荐以下书籍:

逻辑特性表达式

在这个包中,特质应被视为条件为给定类型是否实现了特质的bool值。

可以从这些特质操作中形成表达式

  • 与(&):也称为逻辑合取,当两个操作数都为true时返回true。这通常在 Rust 中通过BitAnd特质定义。

  • 或(|):也称为逻辑析取,当两个操作数中至少有一个为true时返回true。这通常在 Rust 中通过BitOr特质定义。

  • 异或(^):也称为逻辑异或,当两个操作数中只有一个为true时返回true。这通常在 Rust 中通过BitXor特质定义。

  • 非(!):一个取反操作,如果操作数是 true,则返回 false,如果操作数是 false,则返回 true。这通常通过 Rust 中的 Not 特性来定义。

有关这些操作执行的顺序信息,请参阅“优先级和嵌套”

示例

该宏在所有类型上下文中都有效。下面是使用示例。

常量评估

因为类型是 编译时构造,所以该宏的结果可以用作 const 值。

const IMPLS: bool = impls!(u8: From<u32>);

使用 static_assertions,如果特质的表达式评估为 false,则会导致编译失败。

const_assert!(impls!(*const u8: Send | Sync));

优先级和嵌套

特质操作遵循 Rust 的表达式优先级。要定义操作的自定义顺序(例如,从左到右),只需用括号嵌套表达式即可。

let pre = impls!(u64:   From<u8> | From<u16>  ^ From<u32>  & From<u64>);
let ltr = impls!(u64: ((From<u8> | From<u16>) ^ From<u32>) & From<u64>);

assert_eq!(pre, true | true ^ true & true);
assert_ne!(pre, ltr);

互斥

因为异或(^)是特质操作,所以我们可以检查一个类型是否实现了两个特质之一,而不是两个都实现。

struct T;

trait Foo {}
trait Bar {}

impl Foo for T {}

assert!(impls!(T: Foo ^ Bar));

引用类型

许多 Rust 用户可能会感到惊讶,&mut T 并不 实现 Copy 也不 实现 Clone

assert!(impls!(&mut u32: !Copy & !Clone));

你可能会想,这个宏一定是有问题的,因为你一直能够使用 Rust 在整个生命周期中重用 &mut T。这是因为,在某些上下文中,编译器会静默地添加“重新借用”(&mut *ref),具有更短的生存期,并覆盖原始值。实际上,&mut T 是一个只能移动的类型。

未指定大小类型

Rust 中有许多类型没有实现 Sized

// Slices store their size with their pointer.
assert!(impls!(str:  !Sized));
assert!(impls!([u8]: !Sized));

// Trait objects store their size in a vtable.
trait Foo {}
assert!(impls!(dyn Foo: !Sized));

// Wrappers around unsized types are also unsized themselves.
struct Bar([u8]);
assert!(impls!(Bar: !Sized));

泛型类型

当从泛型函数调用时,返回的值基于泛型类型的约束。

use std::cell::Cell;

struct Value<T> {
    // ...
}

impl<T: Send> Value<T> {
    fn do_stuff() {
        assert!(impls!(Cell<T>: Send));
        // ...
    }
}

请注意,这可能导致假阴性。

const fn is_copy<T>() -> bool {
    impls!(T: Copy)
}

assert_ne!(is_copy::<u32>(), impls!(u32: Copy));

生命周期

支持具有生命周期的特质。

trait Ref<'a> {}
impl<'a, T: ?Sized> Ref<'a> for &'a T {}
impl<'a, T: ?Sized> Ref<'a> for &'a mut T {}

assert!(impls!(&'static str:      Ref<'static>));
assert!(impls!(&'static mut [u8]: Ref<'static>));
assert!(impls!(String:           !Ref<'static>));

特性相关类型大小

这个宏实现了一个非常酷(也可以说是诅咒)的功能,在此之前无法实现:使类型的尺寸取决于它实现的特质!请注意,这可能是一个糟糕的想法,不应在生产环境中使用。

在这里,Foo 变为 32 字节,原因仅仅是它实现了 Clone

const SIZE: usize = 32 * (impls!(Foo: Clone) as usize);

#[derive(Clone)]
struct Foo([u8; SIZE]);

assert_eq!(std::mem::size_of::<Foo>(), 32);

bool 返回的 impls! 被转换为 usize 类型,根据其值分别为 1 或 0。如果是 true,则转换为 32 × 1,即 32。这将成为 Foo 中字节数组的长度。

作者

  • Nikolai Vazquez (GitHub: @nvzqz, Twitter: @NikolaiVazquez)

    实现了支持所有逻辑运算符的 impls! 宏,消除了 Nadrieril 的初始 does_impl! 宏的限制。

  • Nadrieril Feneanar (GitHub: @Nadrieril)

    nvzqz/static-assertions-rs#28 中实现了初始的 does_impl! 宏,这个 crate 就是基于这个宏开发的。

许可协议

该项目可以免费使用以下任一许可证:

任您选择。

无运行时依赖