Crates.io | dyn_derive |
lib.rs | dyn_derive |
version | 0.3.4 |
source | src |
created_at | 2024-06-25 19:18:57.513526 |
updated_at | 2024-07-12 01:01:59.33139 |
description | Inherit and derive object-unsafe traits for dynamic Rust. |
homepage | |
repository | https://github.com/shigma/dyn_derive |
max_upload_size | |
id | 1283704 |
size | 63,468 |
Inherit and derive object-unsafe traits for dynamic Rust.
Object safety is a property of traits in Rust that determines whether the trait can be used as a trait object. However, the requirement for object safety is quite strict, limiting the expressiveness of the type system.
For example, you cannot simply write:
// 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.
}
This crate provides a procedural macro for transforming object-unsafe traits into object-safe ones:
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!
}
Although there are still some limitations, this technique works smoothly in my scenarios.
Supertraits is also required to be object-safe if the trait needs to be used as a trait object. However, many useful traits are not object-safe, such as Clone
and PartialEq
.
To tackle this issue, this crate transforms the supertraits into object-safe ones, so that they can be used as supertraits and be derived for your custom types.
Below is a basic example of how to use this 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);
}
Taking the Add
trait as an example:
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")
}
The following std traits are supported:
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
More std traits and custom traits may be supported in the future.
Note: This part is not yet complete.
In Rust, associate functions can be divided into two types: methods and non-methods, depending on whether their first parameter is named 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);
}
This crate supports both methods and non-methods, but they are treated differently. Methods and non-methods are separated into two traits, namely instance and constructor. They are both object-safe.
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>);
}
The original Foo
trait (which may or may not be object-safe) can be wrapped by Instance
and Constructor
types in order to be used as an instance or constructor.
impl FooInstance for ::dyn_std::Instance<Foo> {}
impl FooConstructor for ::dyn_std::Constructor<Foo> {}
If you are developing a library, you may write code like this:
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()));
}
}
And the user of your library may write code like this:
let mut registry = Registry(HashMap::new());
registry.register::<CustomFooImpl>("custom");
A trait should satisfy the all following requirements to be transformed into an object-safe trait by the #[dyn_trait]
attribute.
All supertraits must be:
Sized
will be automatically removed from the supertraits for instance and constructor traits, but retained for the original trait.
It must not have any associated constants.
It must not have any associated types with generics.
Receiver types are types that can be used as the receiver of a method call. The following types can be used as receiver types:
Self
&Self
&mut Self
Box<Self>
Note that Rc<Self>
, Arc<Self>
, and Pin<P>
(where P
is receiver) are not currently supported.
All the parameters must be of the following types:
Self
,Option<T>
, Result<T, E>
, Vec<T>
(where T
, E
are valid parameter types),&dyn
, &mut dyn
, Box<dyn>
of Fn
, FnMut
, FnOnce
(where all the parameters are valid non-referencing parameter types).The following types are valid parameter types:
(Self, &Self, Box<Self>)
HashMap<i32, HashMap<i32, &Self>>
Result<Vec<Box<dyn Fn(Self) -> Self>>, Option<Self>>
The following types are NOT valid parameter types:
&[Self]
Pin<Arc<Self>>
&dyn Fn(&mut Self)
The return type must be a non-referencing parameter type.
Not have any type parameters (although lifetime parameters are allowed).
impl Trait
is considered as a type parameter, thus it is not allowed.
The crate is inspired by the following crates:
MIT.