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
AsRef
AsMut
FromIterator
- 需要:
(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