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过程宏

Download history 79/week @ 2024-03-11 40/week @ 2024-03-18 25/week @ 2024-03-25 61/week @ 2024-04-01 46/week @ 2024-04-08 38/week @ 2024-04-15 45/week @ 2024-04-22 38/week @ 2024-04-29 47/week @ 2024-05-06 74/week @ 2024-05-13 67/week @ 2024-05-20 161/week @ 2024-05-27 161/week @ 2024-06-03 80/week @ 2024-06-10 114/week @ 2024-06-17 125/week @ 2024-06-24

每月483次下载
用于 sized_bitset

MIT 许可证

71KB
1.5K SLoC

logo


Matrix Test crate-name at crates.io crate-name at docs.rs


欢迎使用 Deriving via,这是一个使处理Rust中的Newtypes变得容易的库。这个库提供了一种实用的方法来自动为newtype包装器生成实现,类似于Haskell的GeneralisedNewtypeDerivingDeriving 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::Rcstd::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)");
}

此示例使用通过派生功能。类型Bi32实现中派生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派生到SelfFrom。你还需要指定返回顺序的#[transitive]属性。一些特性需要#[transitive]属性(请参阅可用派生部分)。

注意:From<T> for T是通过泛型实现实现的。

示例(传递性)

以下示例为C派生了AddDisplay。要实现Display,将C解引用到i32就足够了。但是,要实现Add,需要从i32解引用回C。为此,你需要为每个新类型派生From。此外,你还需要使用#[transitive]属性指定从i32C的返回顺序。

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

注意事项

使用类型强制转换的传递情况。据传言,传递的类型强制转换尚不完全支持。

参见: https://doc.rust-lang.net.cn/reference/type-coercions.html#coercion-types

依赖项

~1.4–2MB
~38K SLoC