#traits #partial-eq #clone #dyn #derive

dyn_derive

为动态Rust继承和派生对象不安全特质

14次发布

0.3.4 2024年7月12日
0.3.3 2024年7月9日
0.2.5 2024年7月2日
0.2.3 2024年6月30日
0.1.2 2024年6月27日

#1429 in 过程宏

Download history 28/week @ 2024-06-19 641/week @ 2024-06-26 476/week @ 2024-07-03 182/week @ 2024-07-10 5/week @ 2024-07-17 37/week @ 2024-07-24

830次每月下载
2 crates 中使用

MIT 许可证

59KB
1K SLoC

dyn_derive

为动态Rust继承和派生对象不安全特质。

简介

对象安全性 是Rust中特质的属性,用于确定特质是否可以作为特质对象使用。然而,对象安全性的要求相当严格,限制了类型系统的表达能力。

例如,您不能简单地编写

// Clone is not object-safe
// PartialEq is not object-safe
pub trait Foo: Clone + PartialEq {
    // This method is not object-safe
    fn adjust(self) -> Self;
}

#[derive(Clone, PartialEq)]
pub struct Bar {
    meta: Box<dyn Foo>,         // The trait `Foo` cannot be made into an object.
}

此crate提供了一种过程宏,用于将对象不安全特质转换为对象安全特质

use dyn_derive::*;

#[dyn_trait]
pub trait Foo: Clone + PartialEq {
    fn adjust(self) -> Self;
}

#[derive(Clone, PartialEq)]
pub struct Bar {
    meta: Box<dyn Foo>,         // Now it works!
}

尽管还有一些限制,但这项技术在我的场景中运行良好。

超特质

如果特质需要作为特质对象使用,则超特质也必须是对象安全的。但是,许多有用的特质不是对象安全的,例如 ClonePartialEq

为了解决这个问题,此crate将超特质转换为对象安全特质,以便它们可以作为超特质使用,并为您的自定义类型派生。

基本示例

以下是如何使用此crate的基本示例

use std::fmt::Debug;
use dyn_derive::*;

#[dyn_trait]
pub trait Foo: Debug + Clone + PartialEq {
    fn answer(&self) -> i32 {
        42
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct MetaImpl;

impl Foo for MetaImpl {}

#[derive(Debug, Clone, PartialEq)]
pub struct Bar {
    meta: Box<dyn Foo>,
}

fn main() {
    let foo1 = Bar { meta: Box::new(MetaImpl) };
    let foo2 = Bar { meta: Box::new(MetaImpl) };
    assert_eq!(foo1, foo2);
    let foo3 = foo1.clone();
    assert_eq!(foo3.meta.answer(), 42);
}

不可派生特质

Add 特质为例

use std::fmt::Debug;
use std::ops::Add;
use dyn_derive::*;

#[dyn_trait]
pub trait Foo: Debug + Add {}

#[derive(Debug)]
pub struct MetaImpl(String);

impl Foo for MetaImpl {}

impl Add for MetaImpl {
    type Output = Self;

    fn add(self, rhs: Self) -> Self {
        Self(self.0 + &rhs.0)
    }
}

pub struct Bar {
    pub meta: Box<dyn Foo>,
}

impl Add for Bar {
    type Output = Self;

    fn add(self, rhs: Self) -> Self {
        Self {
            // `Box<dyn Foo>` can be added!
            meta: self.meta + rhs.meta,
        }
    }
}

fn main() {
    let foo1 = Bar { meta: Box::new(MetaImpl("114".into())) };
    let foo2 = Bar { meta: Box::new(MetaImpl("514".into())) };
    let foo3 = foo1 + foo2;
    println!("{:?}", foo3.meta);    // MetaImpl("114514")
}

支持的特质

以下std特质受支持

  • Clone
  • Neg, Not
  • Add, Sub, Mul, Div, Rem
  • BitAnd, BitOr, BitXor, Shl, Shr
  • AddAssign, SubAssign, MulAssign, DivAssign, RemAssign
  • BitAndAssignBitOrAssignBitXorAssignShlAssignShrAssign
  • PartialEqEqPartialOrdOrd

未来可能会支持更多的std特性和自定义特性。

方法

注意:这部分尚未完成。

Rust中,关联函数可以分为两种类型:方法和非方法,这取决于它们是否具有命名参数self

trait Foo: Sized {
  // These are methods.
  fn method_1(&self);
  fn method_2(self) -> Self;
  // These are non-methods.
  fn method_3() -> Option<Vec<Self>>;
  fn method_4(this: &mut Self, that: Self);
}

此crate支持方法和非方法,但它们被处理方式不同。方法和非方法被分为两个特质,即实例构造函数。它们都是对象安全的。

trait FooInstance {
  fn method_1(&self);
  fn method_2(self: Box<Self>) -> Box<dyn FooInstance>;
}

trait FooConstructor {
  fn method_3(&self) -> Option<Vec<Box<dyn FooInstance>>>;
  fn method_4(&self, this: &mut dyn FooInstance, that: Box<dyn FooInstance>);
}

原始的Foo特质(可能或可能不是对象安全的)可以通过InstanceConstructor类型包裹,以便用作实例或构造函数。

impl FooInstance for ::dyn_std::Instance<Foo> {}
impl FooConstructor for ::dyn_std::Constructor<Foo> {}

如果您正在开发库,您可以编写如下代码

use std::collections::HashMap;
use dyn_std::Constructor;

struct Registry(HashMap<String, Box<dyn FooConstructor>>);

impl Registry {
    fn register<T: Foo>(&mut self, name: impl Into<String>) {
        self.0.insert(name.into(), Box::new(Constructor::<T>::new()));
    }
}

而您的库的用户可以编写如下代码

let mut registry = Registry(HashMap::new());
registry.register::<CustomFooImpl>("custom");

规范

一个特质必须满足以下所有要求,才能通过#[dyn_trait]属性转换为对象安全的特质。

超特质

所有超特质必须是

  • 对象安全的
  • 或者是上述标准特质的其中之一

Sized将从实例和构造函数特质中自动删除,但保留在原始特质中。

关联常量

它不得有任何关联常量。

关联类型

它不得有任何带有泛型的关联类型。

关联函数

接收器类型

接收器类型是可以用作方法调用接收者的类型。以下类型可以用作接收器类型

  • Self
  • &Self
  • &mut Self
  • Box<Self>

请注意,Rc<Self>Arc<Self>Pin<P>(其中P是接收器)目前不支持。

参数类型

所有参数都必须是以下类型

  • 不包含Self的类型
  • 接收器类型
  • 有效参数类型的元组
  • Option<T>Result<T, E>Vec<T>(其中TE是有效参数类型)等类型的单子
  • &dyn&mut dynBox<dyn> 类型 FnFnMutFnOnce(其中所有参数都是有效的非引用参数类型)。

以下类型是有效的参数类型

(Self, &Self, Box<Self>)
HashMap<i32, HashMap<i32, &Self>>
Result<Vec<Box<dyn Fn(Self) -> Self>>, Option<Self>>

以下类型不是有效的参数类型

&[Self]
Pin<Arc<Self>>
&dyn Fn(&mut Self)

返回类型

返回类型必须是非引用参数类型。

泛型

没有任何类型参数(尽管允许生命周期参数)。

impl Trait被视为类型参数,因此不允许。

致谢

该项目受到以下crate的启发

许可证

MIT.

依赖

~280–730KB
~17K SLoC