1个不稳定版本
使用旧的Rust 2015
0.0.0 | 2019年12月30日 |
---|
#36 in #test-macro
6KB
判断类型是否实现了逻辑特征表达式?,由 @NikolaiVazquez 提供!
此库定义了 impls!
宏,它返回一个 bool
,表示类型是否在一系列特征上实现了类似布尔的表达式?。
assert!(impls!(String: Clone & !Copy & Send & Sync));
请参阅 "示例" 了解详细用法,如果您敢尝试,还可以查看 "特征依赖类型大小" 中的某些诅咒代码。
索引
推理
作为库作者,确保API保持稳定非常重要。特征实现是API稳定性的一个部分。例如:如果您不小心引入了一个内部类型,使您公开暴露的类型不再为 Send
或 Sync
,那么您现在已经破坏了API,而没有意识到这一点!这种情况最常见的情况是添加一个 原始指针(即 *const T
,*mut T
)作为类型字段。
通过使用 impls!
检查这种情况,无论是在 编译时 还是在单元测试中,您都可以确保不会在不知不觉中做出破坏API的更改,直到为时已晚。
用法
此crate可在 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)的操作。它们通过一系列规则对这些输入标记执行某些操作,将其映射到输出标记。因此,只有它们的输出才被进行类型检查。
如果您想了解宏的实现,我推荐以下资源:
要使用此crate,您不需要了解宏是如何定义的。
特征
在Rust中,特质是一种定义通用属性的方式。它们应该被理解为表达类型能够做什么。例如:如果一个类型为某种类型T
实现了Into
特质,那么我们知道可以通过调用该对象的.into()
方法来将其转换为T
。
如果您想详细了解特质,我推荐以下资源:
逻辑特征表达式
在这个crate中,特质应该被理解为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));
你可能会想,这个宏肯定是有问题的,因为你在整个生命周期中都能重复使用&mut T
。这是因为,在特定上下文中,编译器会默默地添加具有较短生命周期的"re-borrows"(&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);
从宏 impls!
返回的 bool
类型会被转换成 usize
类型,其值为 1 或 0,分别对应于 true
和 false
。如果为 true
,则该值变为 32 × 1,即 32。这将成为结构体 Foo
中字节数组的长度。
工作原理
这利用了内建实现(inherent impl
)的优先级来确定是否实现了某个特质
// Fallback trait for to all types to default to `false`.
trait NotCopy {
const IS_COPY: bool = false;
}
impl<T> NotCopy for T {}
// Concrete wrapper type where `IS_COPY` becomes `true` if `T: Copy`.
struct IsCopy<T>(std::marker::PhantomData<T>);
impl<T: Copy> IsCopy<T> {
// Because this is implemented directly on `IsCopy`, it has priority over
// the `NotCopy` trait impl.
//
// Note: this is a *totally different* associated constant from that in
// `NotCopy`. This does not specialize the `NotCopy` trait impl on `IsCopy`.
const IS_COPY: bool = true;
}
assert!(IsCopy::<u32>::IS_COPY);
assert!(!IsCopy::<Vec<u32>>::IS_COPY);
作者
-
Nikolai Vazquez(GitHub:@nvzqz,Twitter:@NikolaiVazquez)
实现了宏
impls!
,支持所有逻辑运算符,并且没有 Nadrieril 的初始宏does_impl!
的限制。 -
Nadrieril Feneanar(GitHub:@Nadrieril)
在 nvzqz/static-assertions-rs#28 中实现了初始宏
does_impl!
,本仓库最初基于此实现。
许可
该项目可以选择以下任意一种许可证发布:
由您选择。