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 过程宏
830次每月下载
在 2 crates 中使用
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!
}
尽管还有一些限制,但这项技术在我的场景中运行良好。
超特质
如果特质需要作为特质对象使用,则超特质也必须是对象安全的。但是,许多有用的特质不是对象安全的,例如 Clone
和 PartialEq
。
为了解决这个问题,此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
BitAndAssign
,BitOrAssign
,BitXorAssign
,ShlAssign
,ShrAssign
PartialEq
,Eq
,PartialOrd
,Ord
未来可能会支持更多的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
特质(可能或可能不是对象安全的)可以通过Instance
和Constructor
类型包裹,以便用作实例或构造函数。
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>
(其中T
、E
是有效参数类型)等类型的单子 &dyn
、&mut dyn
、Box<dyn>
类型Fn
、FnMut
、FnOnce
(其中所有参数都是有效的非引用参数类型)。
以下类型是有效的参数类型
(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