Expand description
Auto implementation of trait functions by delegation to inner types
This crate provides attribute macros that supports to define trait functions by delegation to inner types.
#[thin_delegate::register]
: Registers definitions of trait, struct and enum.#[thin_delegate::fill_delegate]
: Derives and fillsimpl Trait for StructEnum
by delegation.#[thin_delegate::external_trait_def]
: Imports trait definitions in external crates.
There exist similar crates. See comparison for more details.
See also related RFCs: rfcs#1406, rfcs#2393, rfcs#3530.
§Example
#[thin_delegate::register]
trait AnimalI {
fn sound(&self) -> String;
fn walk(&mut self, pos: usize) -> usize;
}
#[thin_delegate::register]
struct Duck(String);
#[thin_delegate::register]
struct Cat {
sound: String,
speed: usize,
}
#[thin_delegate::register]
enum Animal {
Duck(Duck),
Cat(Cat),
}
// Implement delegatee manually.
impl AnimalI for String {
fn sound(&self) -> String {
self.clone()
}
// String doesn't walk.
fn walk(&mut self, _pos: usize) -> usize {
unimplemented!();
}
}
// Delegate all methods to `String`. Leave `walk()` umimplemented.
// Delegation of a struct with single field is automatic.
#[thin_delegate::fill_delegate]
impl AnimalI for Duck {}
// Delegate `sound()` to `sound: String`. Implement `walk()` manually.
// Delegation of a struct with multiple fields is ambiguous. Needs to designate `scheme`.
#[thin_delegate::fill_delegate(scheme = |f| f(&self.sound))]
impl AnimalI for Cat {
fn walk(&mut self, pos: usize) -> usize {
pos + self.speed
}
}
// Delegate all methods to each arms `Duck` and `Cat`.
// Delegation of an enum is automatic.
#[thin_delegate::fill_delegate]
impl AnimalI for Animal {}
let duck = Duck("quack".to_string());
let mut cat = Cat { sound: "mew".to_string(), speed: 1 };
let mut neko = Cat { sound: "nya-nya-".to_string(), speed: 2 };
assert_eq!(duck.sound(), "quack");
assert_eq!(cat.sound(), "mew");
assert_eq!(cat.walk(10), 11);
assert_eq!(neko.sound(), "nya-nya-");
assert_eq!(neko.walk(10), 12);
let duck = Animal::Duck(duck);
let mut cat = Animal::Cat(cat);
let mut neko = Animal::Cat(neko);
assert_eq!(duck.sound(), "quack");
assert_eq!(cat.sound(), "mew");
assert_eq!(cat.walk(10), 11);
assert_eq!(neko.sound(), "nya-nya-");
assert_eq!(neko.walk(10), 12);
See tests for more examples and sabiniwm for real world examples.
external_trait_def
scheme
- Trait admits
- Generics
- Trait bounds
- GATs
- Only fills not implemented methods
§How it works
#[thin_delegate::register]
defines a declarative macro for each trait/struct/enum definition.#[thin_delegate::fill_delegate]
collects related definitions by using those declarative macros and CPS, and then calls an attribute macro#[thin_delegate::__internal__fill_delegate]
.#[thin_delegate::__internal__fill_delegate]
fillsimpl Trait for StructEnum {...}
.
See src/decl_macro.rs for more details.
§FAQ
§What is an error like error: cannot find macro `__thin_delegate__feed_trait_def_of_Hello` in this scope
?
In the above step 2, #[thin_delegate::fill_delegate]
needs some declarative macros.
This error reports that rustc couldn’t find the macro.
Recommended actions:
- Make sure that your trait/struct/enum is qualified with
#[thin_delegate::register]
correctly. - If you are using an external trait definition, make sure that a path of a module is given by
an argument
external_trait_def
of#[thin_delegate::fill_delegate]
and the module is qualified with#[thin_delegate::external_trait_def]
.
See fail_register_for_*.rs
in tests
for the exact error messages.
§Limitations
There are three types of limitations of thin_delegate
.
- (Normal) limitation
- It’s not feasible to support them.
fail_limitation_*.rs
in tests.
- Intended limitation
- It shouldn’t be supported because there is no way to avoid ambiguity or something wrong.
fail_intended_limitation_*.rs
in tests.
- Weak limitation
- It’s not feasible to support them, but it would be not a problem in practice.
fail_weak_limitation_*.rs
in tests.
§Performance
Note that using enum
is more performant than Box<dyn Trait>
in general case.
(The main reason is not using vtable. One can expect branch prediction works for match
in most-inner loops.)
See also
benchmark of enum_dispatch
.
It would be an option if you need just a polymorphism closed in your application. See
Backend
in sabiniwm
for example (while the main reason for using enum
is not performance in this case).
§Comparison
§enum_dispatch
- Limitations
- Doesn’t support, e.g. external traits and generics.
- Implementation uses not safe mechanism (Using global variable in proc macro)
See also documentation of enum_delegate
.
§enum_delegate (< v0.3.0)
- Limitations
- Doesn’t support, e.g. super traits.
See also limitations.
§enum_delegate (>= v0.3.0)
- Limitations
- Doesn’t support, e.g. super traits, associated const/type.
- Implementation uses very restricted mechanism.
See also limitations.
§auto-delegate
- Limitations
- Doesn’t support, e.g. super traits,
associated const/type,
delegating to
std
types.
- Doesn’t support, e.g. super traits,
associated const/type,
delegating to
- Supports methods without a receiver.
- Implementation uses very restricted mechanism.
§ambassader
- Competitive. I recommend it if you doesn’t need features/APIs of
thin_delegate
.
§portrait
- Exposes a macro with the same name to the struct/enum.
Attribute Macros§
- external_
trait_ def - An attribute macro marking a module as “external trait definitions”
- fill_
delegate - An attribute macro deriving
impl
by delegation to an inner field - register
- An attribute macro registering a definition of trait/struct/enum for
#[thin_delegate::fill_delegate]