4个稳定版本
使用旧Rust 2015
1.0.3 | 2019年12月31日 |
---|---|
1.0.1 | 2019年12月30日 |
在 Rust模式 中排名 #614
每月下载量 3,955
在 19 crates 中使用
42KB
263 行
判断类型是否实现了逻辑特性表达式?,由 @NikolaiVazquez 提供!
此库定义了 impls!
宏?,它返回一个 bool
值,表示类型是否在一系列特性?上实现了布尔表达式。
assert!(impls!(String: Clone & !Copy & Send & Sync));
请参阅“示例”以了解详细用法,如果您够勇敢,请参阅“特性相关类型大小”以查看一些令人诅咒的代码。
索引
推理
作为一个库的作者,确保你的API保持稳定非常重要。特性实现是API稳定性的一个部分。例如:如果你不小心引入了一个内部类型,使你的公开类型不再是 Send
或 Sync
,你现在已经破坏了你的API而没有注意到!这种情况最常见的情况是添加一个 原始指针(即 *const T
,*mut T
)作为类型字段。
通过使用 impls!
检查这种情况,无论是在 编译时间 还是在单元测试中,你可以在太晚之前确保没有做出破坏API的更改。
用法
此软件包可在 crates.io 上获得,并可以通过将以下内容添加到项目的 Cargo.toml
中来使用
[dependencies]
impls = "1"
并添加以下内容到你的crate根目录(main.rs
或 lib.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 就是基于这个宏开发的。
许可协议
该项目可以免费使用以下任一许可证:
任您选择。