27个版本 (稳定)
| 1.6.1 | 2024年2月21日 |
|---|---|
| 1.5.1 | 2023年11月29日 |
| 1.5.0 | 2023年7月10日 |
| 1.1.4 | 2023年3月26日 |
| 0.3.2 | 2023年3月11日 |
#612 在 过程宏 中
每月483次下载
用于 sized_bitset
71KB
1.5K SLoC
欢迎使用 Deriving via,这是一个使处理Rust中的Newtypes变得容易的库。这个库提供了一种实用的方法来自动为newtype包装器生成实现,类似于Haskell的GeneralisedNewtypeDeriving 和 Deriving via 扩展。
deriving via 致力于成为您处理Rust中新类型模式的首选工具。该库利用 DerivingVia 宏生成 Deref 特性实现,允许您的类型通过自动解引用到其基础类型来表现得像智能包装器。
我们的库还引入了诸如使用 #[deriving] 属性进行显式通用NewtypeDeriving,以及使用 #[deriving(Trait(via: Type))] 语法指定为派生生成的基础类型等功能。
根据《Rust参考》,在Rust中,Deref特性通常只用于智能指针。然而,这个库偏离了这一政策。这个库使用Deref特性作为技巧来实现新类型模式。如果你对此方法感到舒适,这个库适合你。
使用DerivingVia宏派生Deref特性
DerivingVia宏生成Deref特性的实现。
一般来说,通过为类型T提供Deref<Target = U>实现,你可以借助Deref转换将类型T的值当作类型U的值来处理。这种机制主要用于包装值的结构体,例如std::rc::Rc或std::boxed::Box。
因此,通过DerivingVia派生的类型将作为底层类型的智能包装器。
示例
DerivingVia宏生成Deref特性实现。因此,即使方法调用在语法上不直接有效,也可以重复引用接收器类型。
use deriving_via::DerivingVia;
#[derive(DerivingVia)]
pub struct Foo(i32);
fn main() {
let foo = Foo(42);
let i: i32 = foo.to_owned(); // This works.
}
Foo没有实现Clone特性,因此foo.to_owned()无法直接使用。然而,Foo实现了Deref特性;因此foo被转换为i32,然后对i32调用to_owned()。
pub struct Foo(i32);
// generated by `[derive(DerivingVia)]` ---+
impl Deref for Foo { // |
type Target = i32; // |
// |
fn deref(&self) -> &Self::Target { // |
&self.0 // |
} // |
} // <-------------------------------------+
fn main() {
let foo = Foo(42);
// This works because of Deref trait.
// ToOwned trait is implemented for i32.
// Foo is dereferenced to i32 and to_owned for i32 is called.
let i: i32 = foo.to_owned();
}
显式泛型新类型派生
可以使用#[deriving]属性进行显式泛型新类型派生。
示例(GND)
use deriving_via::DerivingVia;
#[derive(DerivingVia)]
pub struct A(i32);
#[derive(DerivingVia)]
#[deriving(Display)]
pub struct B(A);
fn main() {
let b = B(A(42));
println!("{b}"); // prints "42"
}
通过派生
使用通过派生功能,可以生成从多层包装类型的特定基础实现中派生的代码。
示例(通过派生)
此示例不使用通过派生功能。
use std::fmt::Display;
use deriving_via::DerivingVia;
#[derive(DerivingVia)]
pub struct A(i32);
#[derive(DerivingVia)]
pub struct B(A);
fn main() {
let b = B(A(42));
// `b.to_string()` uses `A::Display` impl (most nearest impl).
assert_eq!(b.to_string(), "A(42)");
}
此示例使用通过派生功能。类型B从i32实现中派生Display特性。
use std::fmt::Display;
use deriving_via::DerivingVia;
#[derive(DerivingVia)]
pub struct A(i32);
impl Display for A {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "A({})", self.0)
}
}
#[derive(DerivingVia)]
#[deriving(Display(via: i32))] // a new line
pub struct B(A);
fn main() {
let b = B(A(42));
// `b.to_string()` uses `B::Display` impl directly.
assert_eq!(b.to_string(), "42");
}
#[transitive]属性
顺便说一句,当你想要派生Add时,你可以解引用到i32,但不能从i32解引用到Self。因此,你需要从i32派生到Self的From。你还需要指定返回顺序的#[transitive]属性。一些特性需要#[transitive]属性(请参阅可用派生部分)。
注意:From<T> for T是通过泛型实现实现的。
示例(传递性)
以下示例为C派生了Add和Display。要实现Display,将C解引用到i32就足够了。但是,要实现Add,需要从i32解引用回C。为此,你需要为每个新类型派生From。此外,你还需要使用#[transitive]属性指定从i32到C的返回顺序。
use std::fmt::Display;
use deriving_via::DerivingVia;
#[derive(DerivingVia)]
#[deriving(From)]
pub struct A(i32);
#[derive(DerivingVia)]
#[deriving(From)]
pub struct B(A);
#[derive(DerivingVia)]
#[deriving(From, Add(via: i32), Display(via: i32))]
#[transitive(i32 -> A -> B -> C)]
pub struct C(B);
fn main() {
let c: C = C(B(A(42))) + C(B(A(42)));
println!("{c}");
}
可用派生
struct Base(Underlying);
#[derive(DerivingVia)]
#[deriving(<Derive>)]
struct Target(Base);
- fmt
Display- 要求:
Base: Display或(via = <Type>) and Type: Display
- 要求:
- ops
Eq- 要求:
Base: Eq或(via = <Type>) and Type: Eq
- 要求:
Ord- 要求:
Base: Ord或(via = <Type>) and Type: Ord
- 要求:
- 类似
Add的(Add,Sub)- 要求:
Base: From<Underlying> - 限制:单跳或
#[transitive]
- 要求:
- 类似
Mul的(Mul,Div)- 要求:
Base: From<Underlying> - 限制:单跳或
#[transitive]
- 要求:
Arithmetic(Add,Sub,Mul,Div)- 要求:
Base: From<Underlying> - 限制:单跳或
#[transitive]
- 要求:
索引- 需要:
Base: Index或(via = <Type>) 和 Type: Index
- 需要:
IndexMut- 需要:
Base: IndexMut或(via = <Type>) 和 Type: IndexMut
- 需要:
DerefMut- 需要:
Base: DerefMut或(via = <Type>) 和 Type: DerefMut
- 需要:
- hash
Hash- 需要:
Base: Hash或(via = <Type>) 和 Type: Hash
- 需要:
- serde
Serialize- 需要:
Base: Serialize或(via = <Type>) 和 Type: Serialize
- 需要:
Deserialize- 需要:
Base: Deserialize或(via = <Type>) 和 Type: Deserialize
- 需要:
- convert
AsRefAsMutFromIterator- 需要:
(via: <ItemType>)
- 需要:
IntoIterator- 需要:
Base: IntoIterator或(via: <Type>), Type: IntoIterator
- 需要:
Into- 需要:
Base: Into<Underlying> - 限制:单跳或
#[transitive]
- 需要:
From- 限制:单跳或
#[transitive]
- 限制:单跳或
TryFrom- 要求:
Base: From<Underlying> - 限制:单跳或
#[transitive]
- 要求:
FromStr- 要求:
Base: From<Underlying> - 限制:单跳或
#[transitive]
- 要求:
- impls
- Iter
- 需要:
Base: IntoIterator 和 Base 可解引用到切片或(via: <Type>), Type: IntoIterator 和 Type 可解引用到切片
- 需要:
- IntoInner
- 需要:
Base: Clone或(via: <Type>), Type: Clone
- 需要:
- Iter
注意事项
使用类型强制转换的传递情况。据传言,传递的类型强制转换尚不完全支持。
参见: https://doc.rust-lang.net.cn/reference/type-coercions.html#coercion-types
依赖项
~1.4–2MB
~38K SLoC