#traits #object #upcast #macro-generates

macro as-dyn-trait

An attribute macro that generates methods for retrieving supertraits from trait-objects (upcasting)

2 unstable releases

0.2.0 Mar 12, 2020
0.1.0 Mar 9, 2020

#5 in #upcast

Download history 1703/week @ 2023-11-26 1855/week @ 2023-12-03 1852/week @ 2023-12-10 1813/week @ 2023-12-17 1345/week @ 2023-12-24 1371/week @ 2023-12-31 3699/week @ 2024-01-07 2101/week @ 2024-01-14 1720/week @ 2024-01-21 1470/week @ 2024-01-28 1445/week @ 2024-02-04 2383/week @ 2024-02-11 1635/week @ 2024-02-18 2142/week @ 2024-02-25 2283/week @ 2024-03-03 581/week @ 2024-03-10

6,853 downloads per month

Unlicense

38KB
939 lines

An attribute macro that generates methods for retrieving supertraits from trait-objects (upcasting).

If you have a trait with a supertrait, you sometimes want to upcast a trait object. Rust currently does not support this.

trait Super {}

trait Sub: Super {}

fn wants_upcast(sub: Box<dyn Sub>) {
    let s: Box<dyn Super> = sub;
    // do something with super.
}

This results in the following error:

error[E0308]: mismatched types
 --> src/lib.rs:27:29
  |
8 |     let s: Box<dyn Super> = sub;
  |            --------------   ^^^ expected trait `Super`, found trait `Sub`
  |            |
  |            expected due to this
  |
  = note: expected struct `std::boxed::Box<dyn Super>`
             found struct `std::boxed::Box<(dyn Sub + 'static)>`

error: aborting due to previous error

The as_dyn_trait attribute solves this problem:

#[as_dyn_trait]
trait Super {}

trait Sub: Super {}

fn wants_upcast(sub: Box<dyn Sub>) {
    // s has type Box<dyn Super>.
    let s = sub.as_dyn_super();
    // do something with super.
}

To achieve this, the macro generates several traits. For a trait MyTrait, the names of these traits and their methods are:

  • AsDynMyTraitRef:
    • fn as_dyn_my_trait(&self) -> &dyn MyTrait;
    • fn as_dyn_my_trait_mut(&mut self) -> &mut dyn MyTrait;
  • AsDynMyTraitBox:
    • fn as_dyn_my_trait(self: Box<Self>) -> Box<dyn MyTrait>;
  • AsDynMyTraitRc:
    • fn as_dyn_my_trait(self: Rc<Self>) -> Rc<dyn MyTrait>;
  • AsDynMyTraitArc:
    • fn as_dyn_my_trait(self: Arc<Self>) -> Arc<dyn MyTrait>;
  • AsDynMyTraitPinRef:
    • fn as_dyn_my_trait(self: Pin<&Self>) -> Pin<&dyn MyTrait>;
    • fn as_dyn_my_trait_mut(self: Pin<&mut Self>) -> Pin<&mut dyn MyTrait>;
  • AsDynMyTraitPinBox:
    • fn as_dyn_my_trait(self: Pin<Box<Self>>) -> Pin<Box<dyn MyTrait>>;
  • AsDynMyTraitPinRc:
    • fn as_dyn_my_trait(self: Pin<Rc<Self>>) -> Pin<Rc<dyn MyTrait>>;
  • AsDynMyTraitPinArc:
    • fn as_dyn_my_trait(self: Pin<Arc<Self>>) -> Pin<Arc<dyn MyTrait>>;

These traits are automatically implemented for all Sized types that implement MyTrait. If you want to implement MyTrait for dynamically sized types, you need to do add these implementations manually. Since you cannot turn a DST into a trait object, such an implementation must always panic.

In order for those traits to work on trait objects, all of them are automatically supertraits of MyTrait.

The attribute supports several options. The options are passed to the attribute as a comma-separated list:

#[as_dyn_trait(enable_pin = true, trait_name_prefix = DifferentName)]
trait Super {}
  • trait_name_prefix: The prefix to use for the generated traits. Default is AsDyn followed by the trait name.
  • ref_trait_name: The name of the trait for references. Default is the trait name prefix with Ref appended.
  • box_trait_name: The name of the trait for Box<_>. Default is the trait name prefix with Box appended.
  • rc_trait_name: The name of the trait for Rc<_>. Default is the trait name prefix with Rc appended.
  • arc_trait_name: The name of the trait for Arc<_>. Default is the trait name prefix with Arc appended.
  • pin_ref_trait_name: The name of the trait for Pin<_> references. Default is the trait name prefix with PinRef appended.
  • pin_box_trait_name: The name of the trait for Pin<Box<_>>. Default is the trait name prefix with PinBox appended.
  • pin_rc_trait_name: The name of the trait for Pin<Rc<_>>. Default is the trait name prefix with PinRc appended.
  • pin_arc_trait_name: The name of the trait for Pin<Arc<_>>. Default is the trait name prefix with PinArc appended.
  • method_name: The name of the conversion method. Default is the as_dyn_ followed by the trait name (converted to lower-snake-case).
  • mut_method_name: The name of the conversion method for mutable references. Default is the as_dyn_ followed by the trait name (converted to lower-snake-case) and _mut.
  • enable_ref: Enables conversion of references. Default is true.
  • enable_box: Enables conversion of Box<_>. Default is true.
  • enable_rc: Enables conversion of Rc<_>. Default is true.
  • enable_arc: Enables conversion of Arc<_>. Default is true.
  • enable_pin: Enables conversion of the Pin<_> types. These are only generated conversion of the corresponding base pointer is also enabled. Default is false.

This attribute does not use or generate any unsafe code.

Dependencies

~1.5MB
~34K SLoC